How to properly make NonFungibleToken's Unique - corda

Question:
Does the TokenIdentifier of a TokenType used to build a NonFungibleToken have to be a unique string?
Long-Winded Background:
The documentation clearly outlines that “It is, in fact, the responsibility of the developer to ensure that no two instances of an NonFungibleToken refer to the same off-chain or on-chain object…”
I was under the impression that the UniqueIdentifier was how I ensured that uniqueness:
TokenType tokenType = new TokenType("Toyota Corolla", 0);
IssuedTokenType issuedTokenType = new IssuedTokenType(partyA, tokenType);
String VIN = "1G2JB12F047226515";
UUID uuid = UUID.randomUUID();
UniqueIdentifier uniqueIdentifier = new UniqueIdentifier(VIN, uuid);
NonFungibleToken nonFungibleToken = new NonFungibleToken(issuedTokenType, partyA, uniqueIdentifier);
This allows me to define limitless Toyota Corolla’s each with their own unique UUID and Vehicle Identification Number (VIN).
The MoveNonFungibleTokens() flow allows me to specify a QueryCriteria to isolate the specific NonFungibleToken I wish to move (I use a LinearStateQueryCriteria specifying the UUID):
subFlow(new MoveNonFungibleTokens(partyAndToken, observers, queryCriteria));
However when I want to redeem a NonFungibleToken the RedeemNonFungibleTokens() flow only allows me to specify a TokenType:
subFlow(new RedeemNonFungibleTokens(tokenType, issuer, observers));
This means I can’t identify a NonFungibleToken by its UUID. If you do what I am doing above you will receive the following error when I try to redeem:
Exactly one held token of a particular type TokenType(tokenIdentifier=' Toyota Corolla ', fractionDigits=0) should be in the vault at any one time.
If this is the case then TokenType’s tokenIndentifier ("Toyota Corolla") must be the source of uniqueness. I would have to do something like this:
TokenType tokenType = new TokenType("Toyota Corolla-" + UUID.randomUUID(), 0);
Is this correct or have I missed something?
I was just really surprised when I started writing the Redeem parts of my token tests and thought “Well then what is the purpose of the UniqueIdentifier in a NonFungibleToken?”

In the case of non-fungible tokens, the relationship between the token type and the token is one to one; that's why the redeem flow takes only the TokenType parameter.
Looking at the MoveNonFungibleTokens flow input parameters here, they sort of contradict the first statement; because even the comment on the flow states that it should be used for one TokenType at a time, which means the queryCriteria parameter is not needed since you're already specifying the token (token type) that you want to move inside the PartyAndToken parameter. I will forward this discussion to R3 engineers to get a clarification.
As for the your question, the reason you need the unique identifier in the non-fungible token is because it extends LinearState (which is identified by that UUID).
Remember that states are immutable in Corda, so how do you mimic an update? You do it buy using a LinearState, for instance if your non-fungible token is a car and you want to change the owner of the car (i.e. update the holder of the token); then you create a transaction where the input is the current token and the output is the updated token which should have the same linearId as the input; this way the output and the input are tied together and now you can track the history of updates of a certain state by querying that shared linearId.
Side note: Your LinearState should have 2 constructors; one that assigns a random linearId and you should use it when creating a new state, and another constructor that takes a linearId as an input parameter; this constructor should be used when you create the output of an update transaction (so you can create the output with the linearId of the input), also you must mark that constructor with the #CordaSerializable annotation so that Corda uses it when it check-points a certain flow (i.e. serializes then de-serializes your state) otherwise Corda will use the other constructor and assign a new random value for your linearId when it de-serializes your state (when the flow resumes) and you essentially end with a different state!
I recommend that you use EvolvableTokenType for your car example instead of TokenType; this would allow you to add your custom attributes (VIN, price, mileage, etc...) and you can can control which attributes can be updated (price, mileage) and which cannot (VIN); see more about that in the official free Corda course from R3 here.

Related

Implement the constraints requiring the participants to sign the transaction in the IOUContract code

I was going through Corda training material and there is one activity "Impose a constraint on the required signatures in IOUContract.verify". How to implement this constraint requiring both the participant (lender and borrower) to sign the
transaction?
If anybody is aware of then please let me know.
My code:
if (!((command.getSigners().contains(state.getLender().getOwningKey())) && (command.getSigners().contains(state.getBorrower().getOwningKey()))))
throw new IllegalArgumentException("Both lender and borrower together only may sign IOU issue transaction.");
Your code looks legit.
If you cannot pass the test, please make sure the getter of the lender and the borrower are correct in the IOUState class.
If you are uncertain, you can look into this contract class example.
Also, you can check here for an alternative syntax to implement the contract class.
The code you paste in is from the contract of a cordapp. You will initiate the signature collection in the flow of the cordapp.
Let's assume the case that there are only lender and borrower in this transaction (no third party moderator), and the lender is the flow initiator. The lender will draft the state and sign it himself, and sent a half signed transaction to the borrower via a session and the borrower will check it and sign it via the responder.
At that moment you will have a fully signed transaction.
As for code, I would suggest you to take look at this simple negotiation cordapp for reference. https://github.com/corda/samples/blob/release-V4/negotiation-cordapp/workflows-kotlin/src/main/kotlin/negotiation/flows/AcceptanceFlow.kt
val partStx = serviceHub.signInitialTransaction(txBuilder)
// Gathering the counterparty's signature.
val counterparty = if (ourIdentity == input.proposer) input.proposee else input.proposer
val counterpartySession = initiateFlow(counterparty)
val fullyStx = subFlow(CollectSignaturesFlow(partStx, listOf(counterpartySession)))
The state contract will enforce who are the required signers. In this example, the contract is expecting all participants (i.e. lender and borrower) to be required signers of the Create command.
When the flow is crafting a transaction to create a new IOU, it must specify which command to use (Create IOU in our case); and who will sign this command (so the flow will work on collecting those signatures).
The flow will verify the transaction before signing it, which in return will run the contract; if the requested signatures that were specified on the Create command inside the flow don't match what the contract is expecting to see as required signers; then the contract will fail the transaction. Otherwise the contract will pass the transaction, and the flow will proceed to collect the signature of the lender, then collect the signature of the borrower.
So in short, the contract will say who are the required signers for a certain state command, and the flow that's creating a transaction for that state/command combination must obey the contract rules by specifying the same set of signers and collect their signatures.
Thanks, everyone for the detailed explanation. I have implemented the below code and it's working fine now.
List<PublicKey> signers = tx.getCommands().get(0).getSigners();
HashSet<PublicKey> signersSet = new HashSet<>();
for (PublicKey key: signers) {signersSet.add(key);}
List<AbstractParty> participants = tx.getOutputStates().get(0).getParticipants();
HashSet<PublicKey> participantKeys = new HashSet<>();
for (AbstractParty party: participants) {participantKeys.add(party.getOwningKey());}
require.using("Both lender and borrower together only may sign IOU issue transaction.", signersSet.containsAll(participantKeys) && signersSet.size() == 2);

In Corda, which case we should sign transaction with account key rather than node key?

I am learning Account concept which is released in Corda 4.3. The concept also allows node sign the transaction by using account key rather than node key. I look into few aspects and have still questions that:
In which case we should sign transaction with account key rather than node key?
What would be a crucial benefit to use account key signing over node key?
The framework allows transaction between accounts in the same node to be signed with account key. Why should we do that?
Thank you in advance.
It's not about what's the crucial benefit of signing with an account key instead of a node key, it's what your state contract dictates.
For instance if you look at the gold-trading example:
The state has an attribute owningAccount:
data class LoanBook(val dealId: UUID, val valueInUSD: Long,
val owningAccount: PublicKey? = null) : ContractState
The contract dictates that the owningAccount is a required signer for the TRANSFER command:
inputGold.owningAccount?.let {
require(inputGold.owningAccount in command.signers) {
"The account sending the loan must be a required signer" }
}
Notice how the flow signs the transaction with the node's key (because the initiator of a flow is a required signer), and the owningAccount's key (because the contract dictates that):
val locallySignedTx = serviceHub.signInitialTransaction(transactionBuilder,
listOfNotNull(loanBook.state.data.owningAccount,
ourIdentity.owningKey))

Accounts Library: AccountInfoCommand doesn't have an Update command

AccountInfo state has a field called status which is initialized with the value ACTIVE, but currently AccountInfoCommand class only has one command which is Create, so should we use that if we want to write a flow that deactivates an account (i.e. updates it, not creates it)? I don't feel that's right since there should be certain checks that are related to an update command (like there should be one input and one output with same linearId, etc...).
Is there a reason why RequestAccountFlow was designed to return an AccountInfo as opposed to StateAndRef? The latter makes it easier to request an AccountInfo; then use it as an input for a certain transaction (like in my case, I would request an account, get its StateAndRef, clone it with the new status, use the StateAndRef as input, and the clone with new status as output).
With the current Accounts implementation, the AccountInfo state no longer has the status state. https://github.com/corda/accounts/blob/master/contracts/src/main/kotlin/com/r3/corda/lib/accounts/contracts/states/AccountInfo.kt
And the RequestAccountFlow was coded in a way to utilizing ShareAccountInfoFlow (which returns an AccountInfo)

In a Corda transaction are particular state transitions associated with partcular commands?

The commands webinar states (at around 3:10) that "input and output states are always grouped by type and that a command is required for each group." The narrator seems to imply that if a transaction consists of multiple commands then each command will be associated with a distinct subset of the state transitions proposed in the transaction.
However such a view of things does not seem to be captured in the structure of LedgerTransaction. It consists of completely independent lists of inputs, outputs and commands. There's nothing denoting an association between particular commands and particular input or output states.
In my contract code I can group states by type, e.g.:
override fun verify(tx: LedgerTransaction) {
val fooStates = tx.inputsOfType<FooState>()
val barStates = tx.inputsOfType<BarStates>()
But I'm just grouping by choice - I don't have to, and there's nothing tying these groups to particular commands.
So what is the webinar referring to when it says "a command is required for each group"?
The sense behind signatures being associated with commands would be clear if the relationship between commands and state transitions existed as described in the webinar. However in reality one doesn't sign off on particular transitions on a per command basis as the LedgerTransaction class does not capture such relationships.
In the key concepts section on commands one has a coupon command and a pay command and it makes sense that the set of people who have to sign off on the bond state transition may be different to those who need to sign off on the cash state transition. But in the code there's nothing tying a coupon command to the particular states that the signers associated with the command are agreeing to if they sign.
Is this stated requirement that each group must have an associated command just something that the developer should implement in their contract verify logic without being something that one tries to capture in the structure of transactions?
Good question.
You touched on grouping within the contract, and that's correct it is down to the contract implementation, you just need to extend it further to enforce which parties are required to sign depending on the command in the transaction.
So, your verify function might look like the below simplified version of the contract within the Option CorDapp sample:
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands>()
when (command.value) {
is Commands.Issue -> {
requireThat {
val cashInputs = tx.inputsOfType<Cash.State>()
val cashOutputs = tx.outputsOfType<Cash.State>()
"Cash.State inputs are consumed" using (cashInputs.isNotEmpty())
"Cash.State outputs are created" using (cashOutputs.isNotEmpty())
val option = tx.outputsOfType<OptionState>().single()
"The issue command requires the issuer's signature" using (option.issuer.owningKey in command.signers)
}
}
is Commands.Trade -> {
requireThat {
val cashInputs = tx.inputsOfType<Cash.State>()
val cashOutputs = tx.outputsOfType<Cash.State>()
"Cash.State inputs are consumed" using (cashInputs.isNotEmpty())
"Cash.State outputs are created" using (cashOutputs.isNotEmpty())
val inputOption = tx.inputsOfType<OptionState>().single()
val outputOption = tx.outputsOfType<OptionState>().single()
"The transfer command requires the old owner's signature" using (inputOption.owner.owningKey in command.signers)
"The transfer command requires the new owner's signature" using (outputOption.owner.owningKey in command.signers)
}
}
else -> throw IllegalArgumentException("Unknown command.")
}
}
We first pull the command (or commands) from the transaction and that gives us context.
Based on that we can then pull the states we're interested in from either the inputs or outputs e.g. cash/option and begin to check that our constraints are met.
You can find the full version of the above sample at https://github.com/CaisR3/cordapp-option and the contract code can be found in base\src\main\kotlin\net\corda\option\base\contract\OptionContract.kt

Where to check contract constraints

From release-M13 of Corda, in the CordApp-Tutorial example, there are some constraints checks made within the flow itself (ExampleFlow.Acceptor). My question is, what constraints may I check on the flow, and what constraints in the Contract? Or it's just an organization issue?
This is a great question. I believe you are referring to:
#InitiatedBy(Initiator::class)
class Acceptor(val otherParty: Party) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val signTransactionFlow = object : SignTransactionFlow(otherParty) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be an IOU transaction." using (output is IOUState)
val iou = output as IOUState
"The IOU's value can't be too high." using (iou.iou.value < 100)
}
}
return subFlow(signTransactionFlow)
}
}
The CollectSignaturesFlow and its counterpart, the SignTransactionFlow automates the collection of signatures for any type of transaction. This automation is super useful because developers don't have to manually write flows for signature collection anymore! However, developers have to be aware that given any valid transaction - as per the referenced contract code in the transaction - the counter-party will always sign! This is because transactions are validated in isolation, not relative to some expected external values.
Let me provide two examples:
If I have access to one of your unspent cash states from a previous transaction, then perhaps I could create a cash spend transaction (from you to me) and ask you to sign it via the CollectSignaturesFlow. If the transaction is valid then, without any additional checking, you'll sign it which will result in you sending me the cash. Clearly you don't want this!
The contract code can only go part of the way to verifying a transaction. If you want to check that the transaction represents a deal you want to enter into e.g. price < some amount then you'll have to do some additional checking. The contract code cannot opine on what constitutes a commercially viable deal for you. This checking has to be done as part of the SignTransactionFlow by overriding signTransaction
In a production CorDapp, one may wish to defer to human judgement on whether to sign a transaction and enter into a deal. Or alternatively, this process could be automated by reaching out to some external reference data system via HTTP API or MQ to determine if the deal is one that should be entered into.
In the code example above, we added two simple constraints:
One which prevents a borrower from creating an overly large (greater than 100) IOU state
One which ensures the transaction does indeed contain an IOU state and not some other state we are not expecting
Note that these two constraints cannot be placed inside the contract code. Contract code is more appropriate for defining the constraints that govern how an asset or agreement should evolve over time. For example, with regards to an IOU:
Issuances must be signed by lender and borrower
Issuances must be for a value greater than zero
Redemptions must involve a cash payment of the correct currency
The IOU must be redeemed before the expiry date
More examples available here: https://github.com/roger3cev/iou-cordapp-v2/blob/master/src/main/kotlin/net/corda/iou/contract/IOUContract.kt
Remember, Corda is designed for potentially mutually distrusting parties to enter into consensus about shared facts. As such, nodes cannot implicitly trust what they receive over the wire from their counter-parties, therefore we always have to check what we receive is what we expect to receive.
Hope that makes sense!

Resources