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)
Related
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).
I have an contract which consumes an input of typeA to produce output of typeB and my contract looks similar as shown
override fun verify(tx: LedgerTransaction) {
val commandCreate = tx.commands.requireSingleCommand<Commands.Create>()
requireThat {
"One input state should be there for TypeB" using (tx.inputStates.size==1)
"One output states should be there for TypeB" using (tx.outputStates.size==1)
"Input State should be a TypeA" using (tx.getInput(0) is TypeAState)
"Output State should be a TypeB" using(tx.getOutput(0) is TypeBState)
"TypeA id Number should not be empty" using((tx.getInput(0) as TypeAState).idNumber.isNotEmpty())
}
and I get the following error while invoking the flow
java.util.concurrent.ExecutionException:
net.corda.core.contracts.TransactionVerificationException$ContractRejection:
Contract verification failed: Required
com.example.contract.PolicyContract.Commands.Create command, contract:
com.example.contract.PolicyContract, transaction:
B2AE49DEDFE882C9DDBA9ECB35740A689CFDC4F8ED78DD43D912FDC9DC5DC2C4
My flow looks something like this
val txCommand = Command(TypeBContract.Commands.Create(), listOf(me.owningKey))
val txBuilder = TransactionBuilder(notary)
.addInputState(typeARef)
.addOutputState(outputState, TYPEB_CREATION_CONTRACT_ID)
.addCommand(txCommand)
Where am I going wrong??
The problem is the requireSingleCommand. When you create a transaction with input states, the command that the input state was included within another transaction will load here as well.
To solve this use tx.commandsOfType<YourType>() or whatever the syntax is. This will not throw an exception. This solution is what you should use when there are inputs and outputs in a transaction.
The exception is due to single being called in requireSingleCommand.
Consider the following case:
node A builds and signs a TX which is sent to B for signing.
class FlowA(val otherParty: Party) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.getNotary()
val builder = TransactionBuilder(notary)
// ... add some commands and states
val stx = serviceHub.signInitialTransaction(builder)
val session = initiateFlow(otherParty)
subFlow(SendTransactionFlow(session, stx))
return session.sendAndReceive<SignedTransaction>(stx).unwrap {
it.id == stx.id // is it enough?
it
}
}
}
class FlowB(val session: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
subFlow(ReceiveTransactionFlow(session, false))
val stx = session.receive<SignedTransaction>().unwrap {
val ledgerTx = it.toLedgerTransaction(serviceHub, false)
ledgerTx.commandsOfType<SomeContract.Commands.SomeCommand>().single()
ledgerTx.verify() // is it enough?
}
}
}
Is it secure to check only the id of the transaction from the sender side once we received the full signed transaction?
I've read in the doc that id is the root Merkle tree built by using transaction's components, so if the otherParty change something the id would be different, correct?
From the receiver side, is it secure to check which commands are present in the transaction so we are sure that the contract relative to that command is run by means of verify?
On receiving a transaction, you will be able to interrogate all the inputs, outputs and commands.
Typically, the interrogation would happen inside the contracts verify method.
Yes, if anything were to change the root ID would indeed change. Note that it's not possible to change anything within a transaction once it has been signed.
Other things you could look for is the presence of the transaction being notarised.
It depends on what your contract needs to do, but yes, you would check which commands are present within a transaction e.g. in the issuance of a security, you might check that only one issue command is present.
You can also check the commands have received the required counterparty signatures.
If you would like to discuss in person, feel free to join one of office hour sessions - https://www.corda.net/support/technical-office-hours/
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.")
}
I am using #google-cloud/datastore to save data in my entity.I have created the entity with custom key name = id.
How can I have a custom UUID as the key of the entity?
ds.save({
id: <uuid>,
data: Log
}).then(function () {
console.log(entities.map(fromDatastore));
});
const dsKey = ds.key({
namespace : namespace,// optional
path : ([kindName, id])
})
The key can be generated like this. The namespace is optional, if not provided the default namespace will be used. In the path kindname should be provided(if there is a parent kind for the provided kind, the path array value should begin from the root parent name and id) and the id can be any id you assign. If the id is not provided, datastore will generate a random id and assign it to the entity you inserted.
If you meant that you wanted the UUId as the complete key, that is not possible.