I uploaded a file to Corda node and got the following hex value as string back:
854AAE9BE6607CE0B15A70EEBEF19C553557103FB051413F2AA35E70F5B44313
Now I need to pass this as a secureHash parameter to transaction builder:
txBuilder.addAttachment(??).
How to build secure hash from the hex string result obtained from file upload as input parameter to addAttachment..?
SecureHash has toString() function that returns hash as hex string above. I need to create secure hash using the hex string above.
Thanks.
Tried the following update to code:
Added attachId parameter to IOUFlow in Hello World tutorial. Added attachment as txBuilder.addAttachment(attachId). See code below:
class IOUFlow(val iouValue: Int,
val otherParty: Party, val attachId: SecureHash.SHA256) :
FlowLogic<Unit>() {
/** The progress tracker provides checkpoints indicating the progress of
the flow to observers. */
override val progressTracker = ProgressTracker()
/** The flow logic is encapsulated within the call() method. */
#Suspendable
override fun call() {
// We retrieve the notary identity from the network map.
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val txBuilder = TransactionBuilder(notary = notary)
txBuilder.addAttachment(attachId)
....
}
Uploaded attachment to server and got following hash:
C5C84DADD15B2359EBDF0DFC6CCCAA48A0DBA3A04EFD8F03EB117186CC0B2D08
Started flow with following shell command:
start IOUFlow iouValue: 99, otherParty: "O=PartyB,L=New York,C=US”,
attachId: C5C84DADD15B2359EBDF0DFC6CCCAA48A0DBA3A04EFD8F03EB117186CC0B2D08
Shell just responds with '>' and nothing happens. Have to use CTRL-C to get back shell prompt.
Use SecureHash.parse() to convert the string to a SecureHash.
Related
My use case is similar to that of IOU use case. While writing contract for that i am getting the following error.
> Task :contracts:compileKotlin FAILED
e: D:\Capstone_Tryout\contracts\src\main\kotlin\com\template\contracts\TradeContract.kt: (45, 34): Unresolved reference: signers
My code is are
package com.template.contracts
import com.template.states.TradeState
import net.corda.core.contracts.CommandData
import net.corda.core.contracts.Contract
import net.corda.core.contracts.requireSingleCommand
import net.corda.core.contracts.requireThat
import net.corda.core.transactions.LedgerTransaction
// ************
// * Contract *
// ************
class TradeContract :Contract {
companion object {
// Used to identify our contract when building a transaction.
const val ID = "com.template.contracts.TradeContract"
}
// A transaction is valid if the verify() function of the contract of all the transaction's input and output states
// does not throw an exception.
override fun verify(tx: LedgerTransaction) {
val command = tx.commands.requireSingleCommand<Commands>().value
when(command) {
is Commands.Issue -> requireThat {
"There should be no input state" using (tx.inputs.isEmpty())
"There should be one output state" using (tx.outputs.size == 1)
"The output state should be of type TradeState" using (tx.outputs.get(0).data is TradeState)
val outputState = tx.outputs.get(0).data as TradeState
"TradeId should be 10 character long" using (outputState.TradeId.length == 10)
val trade = tx.outputsOfType<TradeState>().single()
"All of the participants must be signers." using (command.signers.toSet() == trade.participants.map { it.owningKey }.toSet())
"A newly issued TradeState must have a positive amount." using (trade.Amount > 0)
"The FromParty and ToParty cannot have the same identity." using (trade.FromParty != trade.ToParty)
}
}
// Verification logic goes here.
}
// Used to indicate the transaction's intent.
interface Commands : CommandData {
class Issue : Commands
}
}
While the similar code is working in corda example.
val iou = tx.outputsOfType<IOUState>().single()
"Both Parties together only may sign Trade issue transaction." using
(command.signers.toSet() == iou.participants.map { it.owningKey }.toSet())
I am not able to figure out why i am getting this error.
Please confirm that you did the following:
Define the command inside your contract:
public interface Commands extends CommandData {
class Create implements Commands {}
}
Extract the command from the transaction inside your verify() method:
final CommandWithParties<Commands.Create> command =
requireSingleCommand(tx.getCommands(), Commands.Create.class);
Command wraps value and signers.
Value is the type of the command
Signers are the public keys of the required signers of the command.
The issue in the code above is :
val command = tx.commands.requireSingleCommand<Commands>().value
Replace the above with:
val command = tx.commands.requireSingleCommand<Commands>()
I already solved this problem, but I would like to understand why it occurred in the first place:
1. I'm using the Java template of Tokens SDK
2. I created my own token type
3. I modified ExampleFlowWithFixedToken class to issue my new token
4. When I ran start ExampleFlowWithFixedToken amount: 100, recipient: PartyB, I got the error: There is a token group with no assigned command
5. Initially my new token class didn't implement the equals() method, when I added it; the error was gone and I was able to issue my token.
Why adding that method, fixes the problem?
public class MyTokenType implements TokenType {
private final int fractionDigits = 6;
private final String tokenIdentifier = "MY_TOKEN";
#NotNull
#Override
public BigDecimal getDisplayTokenSize() {
return BigDecimal.ONE.scaleByPowerOfTen(-fractionDigits);
}
#Override
public int getFractionDigits() {
return fractionDigits;
}
#NotNull
#Override
public Class<?> getTokenClass() {
return this.getClass();
}
#NotNull
#Override
public String getTokenIdentifier() {
return tokenIdentifier;
}
#Override
public boolean equals(Object obj) {
return obj instanceof MyTokenType;
}
}
ExampleFlowWithFixedToken calls the built-in IssueTokens Flow.
This flow , builds the transaction internally specifying input, output states, commands(IssueCommand in this case).
Next step is to verify the contract.
Before verifying the contracts we group the input/output tokens by the issuer.
Each group is then assigned a token command.
This is done because if a transaction contains more than one type of token, they need to grouped by IssuedTokenType.
Also note same token type issued by different issuers are not fungible.
Hence the grouping by IssuedTokenType is required.
Once we have the groups by IssuedTokenType, contract verification is done separately for each group.
When we try to assign a token command to each group , we compare the IssuedTokenType in command to one in our groups.
So if we dont override equals method, none of the IssuedTokenType from the groups will match to the one in the TokenCommand.
Hence the group will not be assigned any TokenCommand.
Each group should at least have one command. If there isn’t then we would not know what to do with that group. Hence it fails saying "There is a token group with no assigned command"
Hope that helps!
I have a Corda flow written in kotlin which takes lambda as an argument:
class ApproveFlow(val arg1: String, val arg2: (Amount<Currency>) -> Amount<Currency>) : FlowLogic<SignedTransaction>()
If I try to invoke the flow over RPC as shown below, Kryo throws an exception:
nodeBProxy.startFlow(::ApproveFlow, "abc", (BasicCalculator()::computeValue))
computeValue function is defined as below:
#CordaSerializable
interface Calculator {
fun computeValue(purchasePrice: Amount<Currency>): Amount<Currency>
}
#CordaSerializable
class BasicCalculator : Calculator {
override fun computeValue(purchasePrice: Amount<Currency>): Amount<Currency>
{ ...
...
return 100.POUNDS
}
}
Exception:
is not annotated or on the whitelist, so cannot be used in serialization corda
Passing a function as a parameter to a flow is probably a bit ambitious. The node needs to evaluate it, after all, and checkpoint it so it can be evaluated later. If you really want to do this you can like so:
val lambda = #CordaSerializable Runnable { whatever() }
or
val lambda = #CordaSerializable fun() { whatever() }
.... but I'd suggest keeping it simple and just passing into plain old data objects. Yes, object serialization is powerful. But with great power comes great responsibility!
Can I find a state with a txhash ? I want something like this : val state = rpcOps.findStateFromTXhash(txhash)
I have found that there is a type of state called linearState that have a linearId property . There is also a hash property but I don't know if it is what I search.
In your flows, you can do getServiceHub().loadState() here you can pass in the securehash to get your state. not sure if we can do something like that directly from CordaRpcConnection object.
Your state will have a linearId if it's a type of linear state. You can search your state using the linearId easily.
read here.
I'd recommend you read more about states to see what best suits your requirement. Link
There is no RPC operation to load a transaction's states given a transaction ID.
However, you can write a flow to do this, as follows, then invoke this flow via RPC:
#InitiatingFlow
#StartableByRPC
class GetStatesFromTransactionFlow(val transactionID: SecureHash) : FlowLogic<List<ContractState>>() {
#Suspendable
override fun call(): List<ContractState> {
val signedTransaction = serviceHub.validatedTransactions.getTransaction(transactionID)
if (signedTransaction == null) {
throw FlowException("Transaction does not exist in node's transaction storage.")
}
val ledgerTransaction = signedTransaction.toLedgerTransaction(serviceHub)
val inputs = ledgerTransaction.inputs.map { it.state.data }
val outputs = ledgerTransaction.outputs.map { it.data }
return inputs + outputs
}
}
Currently we are planning to have one "Draft" version of contract which will not be sent to the counterparty and the initiator can make any changes before send it to the network, so this should be as one "unshared fact". As we know Corda and the vault is used for shared facts, so here I am not sure whether we can still use vault to store this type of "unshared fact", my idea as below and I already can make this work in my local based on the tutorial CorDapp, but would like to get some inputs from other Corda team/experts.
The main change is in the initiator flow:
Initiate the create command's only with the initiator's key
Do not invoke the "CollectSignaturesFlow" so this will not be sent to any others
Invoke "FinalityFlow" after the verify(), so this will be committed to the ledger
Below are the codes for above mentioned points.
override fun call(): SignedTransaction {
// We create a transaction builder
val txBuilder = TransactionBuilder()
val notaryIdentity = serviceHub.networkMapCache.getAnyNotary()
txBuilder.notary = notaryIdentity
// We create the transaction's components.
val ourIdentity = serviceHub.myInfo.legalIdentity
val iou = TemplateState(iouValue, ourIdentity, ourIdentity)
val txCommand = Command(TemplateContract.Create(), listOf(ourIdentity.owningKey))
// Adding the item's to the builder.
txBuilder.withItems(iou, txCommand)
// Verifying the transaction.
txBuilder.toWireTransaction().toLedgerTransaction(serviceHub).verify()
// Signing the transaction.
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
// Finalising the transaction.
return subFlow(FinalityFlow(partSignedTx)).single()
}
You can indeed create an unshared fact in Corda! The key here is in the state's participants list. Just add yourself in the participants list and only your public key to the command. Here's a simple example:
//Contract and State.
class UnsharedFact: Contract {
override val legalContractReference: SecureHash = SecureHash.zeroHash
override fun verify(tx: TransactionForContract) = Unit // Stubbed out.
class Create: CommandData
data class State(val parties: Set<Party>): ContractState {
override val contract: Contract get() = UnsharedFact()
override val participants: List<AbstractParty> get() = parties.toList()
fun addParty(newParty: Party) = copy(parties = parties + newParty)
}
}
// Create flow.
#InitiatingFlow
#StartableByRPC
class CreateUnsharedFact(): FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val me = serviceHub.myInfo.legalIdentity
val notary = serviceHub.networkMapCache.getAnyNotary()
val state = UnsharedFact.State(setOf(me))
val command = Command(UnsharedFact.Create(), listOf(me.owningKey))
val utx = TransactionBuilder(notary = notary).withItems(state, command)
val stx = serviceHub.signInitialTransaction(utx)
return subFlow(FinalityFlow(stx)).single()
}
}
When FinalityFlow is called, you will be the only node that receives the output state.
If you wish to subsequently involve another party then you can create a new version of the state using the addParty method on UnsharedFact.State. Then, create a new transaction, adding the original state as the input and the new version (with the new party) as the output. When this transaction is finalised (notarised) then both parties will have a copy in their respective vaults. Now, I guess the name 'UnsharedFact' is inappropriate :)
You can also remove parties using a similar approach.