The Corda vault API contains a class called CommonQueryCriteria which is implemented like so:
abstract class CommonQueryCriteria : QueryCriteria() {
abstract val status: Vault.StateStatus
open val relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL
open val constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet()
open val constraints: Set<Vault.ConstraintInfo> = emptySet()
open val participants: List<AbstractParty>? = null
abstract val contractStateTypes: Set<Class<out ContractState>>?
open val externalIds: List<UUID> = emptyList()
open val exactParticipants: List<AbstractParty>? = null
override fun visit(parser: IQueryCriteriaParser): Collection<Predicate> {
return parser.parseCriteria(this)
}
}
What is the purpose of the externalIds property?
Note: This surely can't be meant to map UniqueIdentifier.externalId because:
There is a type mismatch (UUID vs. String?).
LinearStateQueryCriteria exists to query states by linearId or externalId.
This was introduced to support querying of accounts back in Corda 4.3:
https://github.com/corda/accounts/blob/master/docs.md#querying-the-vault-by-account
The Vault Query documentation mentions this new attribute right at the bottom of the page when discussing owning keys:
https://docs.corda.net/docs/corda-os/4.8/api-vault-query.html#mapping-owning-keys-to-external-ids
Admittedly it is not very clear and should reference CommonQueryCriteria (not VaultQueryCriteria) and show an example.
Though, if we look at the CreateAccount flow provided by the account library, the AccountInfo is created with a UniqueIdentifier with only an id :
val newAccountInfo = AccountInfo(
name = name,
host = ourIdentity,
identifier = UniqueIdentifier(id = identifier)
)
while the constructor of the UniqueIdentifier has both externalId and id:
data class UniqueIdentifier
constructor(val externalId: String? = null, val id: UUID = UUID.randomUUID())
So, effectively, if it is true that externalId was introduced to support queries with account, the vault queries are actually using the UniqueIdentifier.id of AccountInfo and not its externalId (assuming that nobody is creating an AccountInfo manually overriding the CreateAccount() function, as I suspect).
Related
I have created a Non fungible Evolvable token, shown below
#BelongsToContract(PropertyStateTokenTypeContract::class)
data class PropertyState1(
val landTitle: String,
val location: String,
val price: String,
val maintainer: Party,
override val linearId: UniqueIdentifier,
override val fractionDigits: Int = 0
) : EvolvableTokenType() {
companion object {
val contractId = this::class.java.enclosingClass.canonicalName
}
override val maintainers: List<Party> get() = listOf(maintainer)
}
I have issued this token from a node(PartyA) to an account by using a tokenPointer as shown below
val TestAccount = subFlow(OurAccounts()).filter { it.state.data.name == nameOfAccount }.last()
val anonymous_party = subFlow(RequestKeyForAccount(TestAccount.state.data))
val propertytokentype = a.state.data.toPointer<PropertyState1>()
val PropertyToke = propertytokentype issuedBy ourIdentity heldBy anonymous_party
val issue = subFlow(ConfidentialIssueTokens(listOf( PropertyToke)))
Now I want to transfer this token to an account which is under PartyB So how can I achieve that ?
Step 1.
Prepare query criteria to retrieve the non fungible token from the vault.
QueryCriteria queryCriteria = new QueryCriteria.LinearStateQueryCriteria().withUuid(Arrays.asList(tokenId)).withStatus(Vault.StateStatus.UNCONSUMED);
Step 2.
Retrieve the non fungible token from the vault.
// grab the ticket off the ledger
StateAndRef<T20CricketTicket> stateAndRef = getServiceHub().getVaultService().
queryBy(T20CricketTicket.class, queryCriteria).getStates().get(0);
Step 3: get the pointer to the token
T20CricketTicket evolvableTokenType = stateAndRef.getState().getData();
//get the pointer to the T20CricketTicket
TokenPointer tokenPointer = evolvableTokenType.toPointer(evolvableTokenType.getClass());
Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
Step 4 : create a transactionBuilder
//create a transactionBuilder
TransactionBuilder transactionBuilder = new TransactionBuilder(notary);
Step 5: transfer the non fungible token from one account to other
//transfer the non fungible token from seller to buyer
//this add inputs and outputs to transactionBuilder
MoveTokensUtilitiesKt.addMoveNonFungibleTokens(transactionBuilder, getServiceHub(), tokenPointer, buyerAccount);
Complete sample is available in the Corda repo here.
Hope that helps.
The Accounts library has 2 good examples about using accounts with Tokens SDK:
https://github.com/corda/accounts/tree/master/examples/tokens-integration-test/src/integrationTest/kotlin/com/r3/corda/lib/accounts/examples/tokensTest
Here they transfer a fungible token between 2 accounts, in your case use the non-fungible version of the flow (i.e. MoveNonFungibleTokens): https://github.com/corda/accounts/blob/32e7ae0044ce3b7c8640154dc333e61261251371/examples/tokens-integration-test/src/integrationTest/kotlin/com/r3/corda/lib/accounts/examples/tokensTest/IntegrationTest.kt#L134
Also make sure that you specify the changeHolder in the flow; if your sending account has 1 token with quantity 5 and you only want to send quantity 2, then the change must go back to your sending account, if you leave the changeHolder empty; the MoveNonFungibleTokens flow will assign the change to the initiating node.
In Corda HelloWorld example following state is defined:
class IOUState(val value: Int,
val lender: Party,
val borrower: Party) : ContractState {
override val participants get() = listOf(lender, borrower)
}
To query for IOUState in the vault using RPC, I want to use the following API:
vaultQuery(contractStateType: Class<out t>) Vault.Page<T>
what parameter should be passed to vaultQuery?
Tried using the following:
vaultQuery(IOUState)
but following is reported:
“Classifier “IOUState” does not have a companion object and
thus must be initialized here”
Using following does not work either
(IOUState(0, null, null))
What parameter should be passed that is of type:
Class<out IOUState)
The following worked:
vaultQuery(IOUState::class.java)
vaultQuery(BillState::class.java)
To get state I can use Vault, but what about transactions? How I can get them, for example, by txHash? Is it possible to do this by CordaRPCOps, there is internalVerifiedTransactionsSnapshot method, but it is deprecated now.
First, note that as of Corda 3, there are no stability guarantees regarding the behaviour of any method to retrieve a transaction or its dependencies. In particular, we cannot guarantee that the set of transactions retrieved will not change across Corda versions.
This is because in future versions of Corda, nodes will likely only exchange transaction chains in SGX-encrypted form. These transaction chains will then be verified inside an SGX enclave on the node. This will prevent nodes from seeing the contents of the transactions they are verifying (see the blogpost here: https://www.corda.net/2017/06/corda-sgx-privacy-update/). This may even go so far as to only allow nodes to see certain parts of the transactions they are signing.
Ways to retrieve transactions as of Corda 3
1. Using CordaRPCOps.internalVerifiedTransactionsSnapshot
If you are interacting with the node via RPC, CordaRPCOps.internalVerifiedTransactionsSnapshot returns a list of all recorded transactions.
If you only wanted to get a single transaction and you knew its hash, you could write:
val transactions = cordaRPCOps.internalVerifiedTransactionsSnapshot()
val signedTransaction = transactions
.find { it.id == transactionHash }
?: throw IllegalArgumentException("Unknown transaction hash.")
Note that the transactions returned are of type SignedTransaction. This form does not contain the transaction's attachments or inputs (only the attachment hashes and input state references).
To retrieve a transaction's attachments via RPC, you could write:
val transactions = cordaRPCOps.internalVerifiedTransactionsSnapshot()
val signedTransaction = transactions
.find { it.id == transactionHash }
?: throw IllegalArgumentException("Unknown transaction hash.")
val attachmentHashes = signedTransaction.tx.attachments
val attachmentStreams = attachmentHashes.map { hash -> cordaRPCOps.openAttachment(hash) }
And to retrieve a transaction's inputs via RPC, you could write:
val transactions = cordaRPCOps.internalVerifiedTransactionsSnapshot()
val signedTransaction = transactions
.find { it.id == transactionHash }
?: throw IllegalArgumentException("Unknown transaction hash.")
val inputStateRefs = signedTransaction.inputs
val inputStates = inputStateRefs.map { stateRef ->
val transaction = transactions.find { it.id == stateRef.txhash }
?: throw IllegalArgumentException("Unknown transaction hash.")
transaction.tx.outputStates[stateRef.index]
}
2. Using the ServiceHub
If you are in a situation where you have access to the node's ServiceHub (e.g. within a flow or a Corda service), you can use serviceHub.validatedTransactions.track().snapshot to get all transactions, and serviceHub.validatedTransactions.getTransaction(transactionHash) to get a specific transaction by hash.
Note that the transactions returned are of type SignedTransaction. This form does not contain the transaction's attachments or inputs (only the attachment hashes and input state references).
To convert the SignedTransaction to a LedgerTransaction (where the attachments and inputs are resolved), you could write:
val signedTransaction = serviceHub.validatedTransactions.getTransaction(transactionHash)
val ledgerTransaction = signedTransaction.toLedgerTransaction(serviceHub)
3. By connecting to the node's database
You can connect directly to the SQL database backing the node, and retrieve the transactions using an SQL query.
That's right, although please note that the ServiceHub and SQL approaches are basically the same thing as the deprecated RPC and may also stop working in future (or not, depending on how we manage the transition to an encrypted ledger).
There are other approaches you can use. For instance you could aggregate the bits of history you care about up into the latest version of the state. This also lets you restrict the view of the history once SGX lands.
The first solution (Using CordaRPCOps.internalVerifiedTransactionsSnapshot) is really slow.
It is exist one more way to get transaction history and it is pretty effective.
You can do it by using rpcOps.vaultQueryBy
fun transaction(transactionId: String): List<Vault.Page<ContractState>> {
// get jdbc connection (you may simplify it within cordapp)
val jt = jt()
// get all states of transaction
val output_indexes = jt.queryForList("SELECT OUTPUT_INDEX FROM VAULT_STATES WHERE transaction_id = '$transactionId'", Int::class.java)
val transactionHash = SecureHash.parse(transactionId)
// get Rpc connection
val rpcOps = initialiseNodeRPCConnection()
val transactionStates = output_indexes.map {
val constraintTypeCriteria = QueryCriteria.VaultQueryCriteria(status = Vault.StateStatus.ALL, stateRefs = listOf(StateRef(transactionHash, it)))
rpcOps.vaultQueryBy<ContractState>(constraintTypeCriteria)
}
return transactionStates
}
We have a queryablestate for storing some information when the system is initialized, these states are issued once and never consumed
class DataState implements LinearState, QueryableState {
Party partyA;
Party partyB;
String partyAId;
String partyBId;
String sharedIdentifierNumber;
DataState(Party partyA, Party partyB, String partyAId, String partyBId, String sharedIdentifierNumber) {
this.partyA = partyA;
this.partyB = partyB;
this.partyAId = partyAId;
this.partyBId = partyBId;
this.sharedIdentifierNumber = sharedIdentifierNumber;
}
}
partyA and partyAId must be related to entity A (same for partyB)
some example instances:
new DataState(party1, party2, "00001", "12345", "0001")
new DataState(party3, party1, "05432", "00001", "0022")
new DataState(party2, party1, "12345", "00001", "0123")
we want to have methods that work like a map:
String retrievePartyId(Party party){}
assertTrue(retrievePartyId(party1).equals("00001"))
Party retrieveParty(String partyId){}
assertTrue(retrieveParty("12345").equals(party2))
we have already done this by querying all the states with custom field criteria and comparing through iteration on the
List<StateAndRef<DataState>>
We would like to know if there is some efficient way of doing this, maybe with some custom querycriteria in Corda. We think this is related to sql column projections. The query interface returns list of 'states(.getStates())' OR aggregation results '(.getOtherResults())'. We were wondering if it’s possible (or planned) to get a single column from the db and then filter the duplicates through the vault interface, currently we’re doing that in java.
If your states are QueryableStates, they can be efficiently queried based on their schema's attributes.
For example, suppose in the CorDapp Example (https://github.com/corda/cordapp-example), I wanted to query the vault for IOUStates based on their value and whether I am the lender. I could do this as follows:
try {
Field value = IOUSchemaV1.PersistentIOU.class.getDeclaredField("value");
Field lender = IOUSchemaV1.PersistentIOU.class.getDeclaredField("lender");
QueryCriteria valueOver50Criteria = new QueryCriteria.VaultCustomQueryCriteria(greaterThan(value, 50));
QueryCriteria lenderIsMeCriteria = new QueryCriteria.VaultCustomQueryCriteria(equal(lender, getOurIdentity().getName().toString()));
QueryCriteria criteria = valueOver50Criteria.and(lenderIsMeCriteria);
Vault.Page<IOUState> results = getServiceHub().getVaultService().queryBy(IOUState.class, criteria);
List<StateAndRef<IOUState>> matchingStates = results.getStates();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
In your case, you would be querying based on the party's ID or the party's name. You would then take the first result and use it to map the party's name to the party's ID, or vice-versa.
In Corda, is there a way to refer to an unspent state in a transaction without spending it? The aim is to allow the contract to use some of the information in the state being referred to as part of the verify method.
Reference states will be added to Corda Version 4 which will be released later this year. This solves the problem above. See:
https://github.com/corda/corda/blob/master/docs/source/design/reference-states/design.md
There is no built-in support for this pattern in Corda at this time, but it will be added in Corda 4 (see Roger's answer below). For now, you have several options:
Writing the states' contracts to allow this behaviour:
You can add a command to the contract which enforces the requirement that there is a matching output for each input of the state type you wish to reference. This guarantees that the transaction is only referencing the state, and not modifying it. Here is an example:
class MyContract : Contract {
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<MyContract.Commands>()
when (command.value) {
is Commands.Reference -> requireThat {
val inputs = tx.inputsOfType<MyState>()
val outputs = tx.outputsOfType<MyState>()
// Assuming `MyState.equals` has been overridden appropriately.
"There must be a matching output state for each input state" using
(inputs.toSet() == outputs.toSet())
}
}
}
interface Commands : CommandData {
class Reference: Commands
}
}
Referring the state as a field in an input state, output state or command:
You can include the reference state in the transaction as a field on an input state, output state or command. A command is likely to be the best fit:
interface Commands : CommandData {
class Reference(val referenceState: MyState): Commands
}
You can then check the contents of this state within the contract's verify method. For example:
class MyContract : Contract {
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<MyContract.Commands>()
when (command.value) {
is Commands.Reference -> requireThat {
val commandData = command.value as Commands.Reference
val referenceState = commandData.referenceStateAndRef.state.data
val inputs = tx.inputsOfType<MyState>()
"The input state contents must match the reference data" using
(inputs.all { it.contents == referenceState.contents })
}
}
}
interface Commands : CommandData {
class Reference(val referenceStateAndRef: StateAndRef<MyState>): Commands
}
}
With this approach, you also have to check in the flow that the reference state is identical to the state actually on the ledger (i.e. that the transaction's proposer hasn't added a fake state object as a reference). For example:
val referenceCommand = ledgerTransaction.commandsOfType<Reference>().single()
val referenceStateAndRef = referenceCommand.value.referenceStateAndRef
val actualStateAndRefFromVault = serviceHub.toStateAndRef<MyState>(referenceStateRef)
if (referenceStateAndRef != actualStateAndRefFromVault) {
throw FlowException("Referenced state does not match state in the vault.")
}