Question about attachment upload and download - corda

First I have read the doc from corda about using attachment. However I still have question about the process of uploading and downloading attachment.
My task was to write a simple cordapp for transferring file from NodeA to NodeB. After uploading a zip file from NodeA shell, I received a hash and then included this in the transaction. The flow was successful. However, in NodeB, I could not get the file back. Then I tried to get the file back from NodeA using that hash. However, the shell returned error message and said invalidInputSteam.
But then when I ran cordaftp (https://github.com/corda/cordaftp) and tried to upload a file and download this from the same shell, the shell correctly asked the path for storage. I had read various posts and knew that I need to include extra codes for successfully downloading. But I have no idea which file I should amend and what code I should write. Hope someone can help me solve my problem as I already spent days on reading previous posts and doc.
Here below is the flow part:
#InitiatingFlow
#StartableByRPC
class FileInitiateFlow(
val receiver: Party,
val comment: String,
val hash: SecureHash.SHA256) : FlowLogic<SignedTransaction>() {
companion object {
object GENERATING_TRANSACTION : Step("Generating transaction")
object VERIFYING_TRANSACTION : Step("Verifying contract constraints.")
object SIGNING_TRANSACTION : Step("Signing transaction with sender private key.")
object GATHERING_SIGS : Step("Gathering the receiver's signature."){
override fun childProgressTracker() = CollectSignaturesFlow.tracker()
}
object FINALISING_TRANSACTION : Step("Obtaining notary signature and recording transaction.") {
override fun childProgressTracker() = FinalityFlow.tracker()
}
fun tracker() = ProgressTracker(
GENERATING_TRANSACTION,
VERIFYING_TRANSACTION,
SIGNING_TRANSACTION,
GATHERING_SIGS,
FINALISING_TRANSACTION
)
}
override val progressTracker = tracker()
#Suspendable
override fun call(): SignedTransaction {
// Obtain a reference to the notary we want to use.
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val sender = serviceHub.myInfo.legalIdentities.first()
// Stage 1.
progressTracker.currentStep = GENERATING_TRANSACTION
// Generate an unsigned transaction.
val fileState = FileState(sender, receiver,comment)
val txCommand = Command(RoamingContract.Commands.FileInitiate(), fileState.participants.map { it.owningKey })
val txBuilder = TransactionBuilder(notary)
.addOutputState(fileState, ID)
.addCommand(txCommand)
.addAttachment(hash)
// Stage 2.
progressTracker.currentStep = VERIFYING_TRANSACTION
// Verify that the transaction is valid.
txBuilder.verify(serviceHub)
// Stage 3.
progressTracker.currentStep = SIGNING_TRANSACTION
// Sign the transaction.
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
// Stage 4.
progressTracker.currentStep = GATHERING_SIGS
// Send the state to the counterparty, and receive it back with their signature.
val otherPartyFlow = initiateFlow(receiver)
val fullySignedTx = subFlow(CollectSignaturesFlow(partSignedTx, setOf(otherPartyFlow), GATHERING_SIGS.childProgressTracker()))
// Stage 5.
progressTracker.currentStep = FINALISING_TRANSACTION
// Notarise and record the transaction in both parties' vaults.
return subFlow(FinalityFlow(fullySignedTx, FINALISING_TRANSACTION.childProgressTracker()))
}
}
#InitiatedBy(FileInitiateFlow::class)
class FileInitiateRespond(val senderFlow: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction{
val signedTransactionFlow = object : SignTransactionFlow(senderFlow) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
val output = stx.tx.outputs.single().data
"This must be an File State" using (output is FileState)
}
}
return subFlow(signedTransactionFlow)
}
}
So I first run uploadAttachment for uploading the zip file, I got the hash and then start the flow, with hash as input. The flow was succeeded, but in receiver's side, I could not get the hash key for the uploaded file from checking existing state.

Attachment hashes of instances of SecureHash. There is a bug in Corda 3.2/3.3 whereby the shell cannot convert strings into SecureHash objects.
The fix is here: https://github.com/corda/corda/pull/3248 and will be available in Corda 4.

Related

Unable to Create Corda Transaction with self node using corda 4.0

I want to create transaction with self node using corda 4.0 .
I used sample IOU example for this and added my changes in ExampleFlow as shown in below code .
https://github.com/corda/cordapp-example/tree/release-V4/java-source.
But its not allowing to create transaction with self node.
also I followed/implemented the answers from this Corda 4 - Single Party Transaction Failed to Commit to Ledger
but it didnt work out.
I made Only changes in ExampleFlow as shown below / Rest of the code from iOU example is same.
Please help.
#Suspendable
override fun call(): SignedTransaction {
// Obtain a reference to the notary we want to use.
val notary = serviceHub.networkMapCache.notaryIdentities[0]
// Stage 1.
progressTracker.currentStep = GENERATING_TRANSACTION
// Generate an unsigned transaction.
val iouState = IOUState(iouValue, serviceHub.myInfo.legalIdentities.first(), otherParty)
val txCommand = Command(IOUContract.Commands.Create(), listOf(ourIdentity.owningKey))
val txBuilder = TransactionBuilder(notary)
.addOutputState(iouState, IOU_CONTRACT_ID)
.addCommand(txCommand)
// Stage 2.
progressTracker.currentStep = VERIFYING_TRANSACTION
// Verify that the transaction is valid.
txBuilder.verify(serviceHub)
// Stage 3.
progressTracker.currentStep = SIGNING_TRANSACTION
// Sign the transaction.
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
// Stage 5.
progressTracker.currentStep = FINALISING_TRANSACTION
// Notarise and record the transaction in both parties' vaults.
return subFlow(FinalityFlow(partSignedTx,emptyList()))
}
I believe the issue is because you are using the otherparty in the state:
val iouState = IOUState(iouValue, serviceHub.myInfo.legalIdentities.first(), otherParty)
Corda 4 requires that all participants flowsession must be provided in the FinalityFlow, so that the state information could be distributed properly.
Refer here for more
Flow Sessions we're not provided for following transaction participants - Corda 4

Corda How to Transact a Partially Signed transaction corda 4?

I am trying to do the following scenario ,
There is a basic IOU transaction which is done from Party A to Party B in which Party A who is the initiator will only sign the transaction , Party B just accepts it(without signing) . To achieve the following I did the following things which worked fine in Corda 3 using the IOU flow of the corda samples project.
1.In the transaction command , I just passed only the Initiator owning key instead of sending both the participants owning keys
I removed the "All Signers" check from the contract
I removed the "Gather counter party signature" Step.
When I moved the same to corda 4 , I noticed the following
Since I am not setting the acceptors owning key as part of the
txcommand , The transaction is saved in the initiator but not part
of the acceptor because of session issue which I understood from the
following.reference
Looking at the reference I rectified it accordingly, now Corda is expecting the signing of the acceptor also which is against my use case, if I do not add , it is throwing the following error
'net.corda.core.transactions.SignedTransaction$SignaturesMissingException:Missing
signatures on transaction '
Please let me know if any workaround is present on the same.
P.S : I am using the cordapp-example Java code
Here is the sample flow which achieves the same functionality
#InitiatingFlow
#StartableByRPC
class IssuerRegistration(
val issuerData: IssuerData
) : BaseFlow<String>() {
override val progressTracker = ProgressTracker()
#Suspendable
override fun call(): String {
// We retrieve the notary identity from the network map.
val notary = firstNotary
// We create the transaction components.
val meta_Info = Meta_Info("pdf", Utils.getCurrentDateTime(), Utils.getCurrentDateTime())
val ids = Ids(issuerData.ids.dunsId)
//create list of all parties involved in this flow
val partList = ArrayList<Party>()
partList.add(ourIdentity)
partList.add(createParty("partyb", serviceHub))
//random unique id of the state
val userUniqueId = UUID.randomUUID()
//create output state actually this will
//save to related parties vault
val outputState = IssuerState(userUniqueId.toString(),
issuerData.company_id,
issuerData.company_name,
issuerData.company_symbol,
ids,
meta_Info,
partList)
//registration command will be verified by issuercontract
val command = Command(RegistrationCommand.IssuerRegistration(),
ourIdentity.owningKey)
// We create a transaction builder and add the components.
val txBuilder = TransactionBuilder(notary = notary)
.addOutputState(outputState, RegistrationContract.ID)
.addCommand(command)
// Verifying the transaction.
txBuilder.verify(serviceHub)
// Signing the transaction. from sender side
val signedTx = serviceHub.signInitialTransaction(txBuilder)
val listOfSession = ArrayList<FlowSession>()
//creating all other party session to get
//signature from to validate the transaction
val otherPartySession = initiateFlow(otherPartySession)
listOfSession.add(otherPartySession)
//end the flow and write the transation data to related parties vault
subFlow(FinalityFlow(signedTx, listOfSession))
return userUniqueId.toString()
}
}
// Replace Responder's definition with:
#InitiatedBy(Flows.IssuerRegistration::class)
class IssueResponder(private val otherPartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
subFlow(ReceiveFinalityFlow(otherPartySession))
}
}
Issue was on the acceptor side , since I was using the SignTransactionFlow which expects the participants to all be signers. Once I commented the code and just ran the ReceiveFinalityFlow it worked fine. Reference : SignTransactionFlow

Reading data from ENV variables in a subflow

I am trying to make an API call from a sub-flow which is annotated with #InitiatedBy. Is there any way to read the API URL from an ENV variable in a sub-flow, instead of hard-coding it?
For example:
#InitiatedBy(Initiator::class)
class Acceptor(val otherPartyFlow: FlowSession) : FlowLogic<SignedTransaction>() {
#Suspendable
override fun call(): SignedTransaction {
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val txID = otherPartyFlow.receive<SecureHash>().unwrap { secureHash -> secureHash }
val commitedId = waitForLedgerCommit(txID)
val op = commitedId.tx.outputStates.single() as RequestState
val txBuilder = TransactionBuilder(notary)
try {
val res = khttp.get("http://localhost:3000/getTitle", timeout = 30.0).jsonObject.getString("data")
val iouState = IOUState(res, serviceHub.myInfo.legalIdentities.first(), otherPartyFlow.counterparty)
val txCommand = Command(RequestContract.Commands.Approve(), ourIdentity.owningKey)
val txCommand1 = Command(IOUContract.Commands.Create(), ourIdentity.owningKey)
txBuilder.addInputState(commitedId.tx.outRefsOfType<RequestState>().single())
txBuilder.addOutputState(iouState, IOU_CONTRACT_ID)
txBuilder.addOutputState(op.copy(status = "Transferred"), RequestContract.REQUEST_ID)
txBuilder.addCommand(txCommand)
txBuilder.addCommand(txCommand1)
// Verify that the transaction is valid.
txBuilder.verify(serviceHub)
// Stage 3.
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
return subFlow(FinalityFlow(partSignedTx))
}catch (ex: Exception){
logger.info(ex.message)
txBuilder.addInputState(commitedId.tx.outRefsOfType<RequestState>().single())
val txCommand1 = Command(RequestContract.Commands.Expire(), ourIdentity.owningKey)
txBuilder.addCommand(txCommand1)
txBuilder.verify(serviceHub)
// Stage 3.
val partSignedTx = serviceHub.signInitialTransaction(txBuilder)
return subFlow(FinalityFlow(partSignedTx))
}
}
}
The node on which the Acceptor flow is executed needs to retrieve some data from its external system over API. Currently it is hardcoded in the sub-flow code. Is it possible for a sub-flow to read the URL (http://localhost:3000/getTitle) from ENV variable / node.conf file?
In Corda 4+
Corda 4 will introduce the concept of CorDapp configuration files that will allow you to provide CorDapp-specific configuration.
These configuration files must be placed in the <node_dir>/cordapps/config folder. These files are loaded when a CordappContext is created and so can change during runtime.
The name of the file should match the name of the CorDapp JAR (e.g. if your CorDapp is called hello-0.1.jar the config file should be called config/hello-0.1.conf). The config files must be written in the Typesafe/Lightbend config format.
CorDapp configuration can be accessed from CordappContext::config whenever a CordappContext is available.
Corda 3 alternatives
In the meantime, in Corda 3, your best options are either to create your own local configuration files (see here), or to store the configuration in your node database (see here).

$$Error:Counterparty sent session rejection message at unexpected time with message class flow is not registered

I want to make external service call by using oraclizer. So I m trying to run the example file given below:
Example Oraclize
After running, I'm getting an error--
Counterparty sent session rejection message at unexpected time with message class it.oraclize.cordapi.flows.OraclizeQueryFlow is not registered
In the flow, OraclizeQueryAwaitFlow flow is called which in turn calls OraclizeQueryFlow. In this flow there is a sendAndReceive call which happens as follows:
#InitiatingFlow
#StartableByRPC
class OraclizeQueryFlow (val datasource: String, val query: Any, val proofType: Int = 0, val delay: Int = 0) : FlowLogic<String>() {
companion object {
object PROCESSING : ProgressTracker.Step("Submitting the query.")
#JvmStatic
fun tracker() = ProgressTracker(PROCESSING)
#JvmStatic
val console = loggerFor<OraclizeQueryFlow>()
}
override val progressTracker = tracker()
fun console(a: Any) = loggerFor<OraclizeQueryFlow>().info(a.toString())
// start OraclizeQueryFlow datasource: "URL", query: "json(https://min-api.cryptocompare.com/data/price?fsym=USD&tsyms=GBP).GBP", proofType: 16, delay: 0
// start OraclizeQueryFlow datasource: identity, query: hello, proofType: 0, delay: 0
#Suspendable
override fun call(): String {
val oraclize = serviceHub.identityService
.wellKnownPartyFromX500Name(OraclizeUtils.getNodeName()) as Party
progressTracker.currentStep = PROCESSING
val session = initiateFlow(oraclize)
val query = Query(datasource, query, delay, proofType)
val queryId = session.sendAndReceive<String>(query).unwrap { it }
console("Query id: $queryId")
return queryId
}
}
On checking logs I happened to find out that the sendAndReceive call is throwing this error.
This is Mauro from Oraclize, are you using the public testnet of Corda? If not, you should follow this guide to be able to use our service. If you are using it, then you should wait a couple of working days as we are still testing the service on the new testnet.

Why do I get initiator of CollectSignaturesFlow must pass in exact sessions error in Corda?

I am making a CorDapp that involves two parties - a Client and an Underwriter. I have two main flows, one being IssuePolicy and another being PayoutPolicy. When I run each flow once, there are no issues. When I run IssuePolicy again, I receive the error: The Initiator of CollectSignaturesFlow must pass in exactly the sessions required to sign the transaction. I've already searched Stack Overflow and found this post: Flow Exception in CollectSignaturesFlow but I believe I have signed with one party and initiated flow with the other, so I am not sure whether this solution applies to me. Any help would be appreciated!
IssuePolicy Flow:
// Step 3. Building.
progressTracker.currentStep = BUILDING
val notary = serviceHub.networkMapCache.notaryIdentities[0]
val utx = TransactionBuilder(notary = notary)
.addOutputState(policy, INSUREFLIGHT_CONTRACT_ID)
.addCommand(InsureFlightContract.Commands.Issue(), policy.participants.map { it.owningKey })
.setTimeWindow(serviceHub.clock.instant(), 30.seconds)
// Stage 4. Get some cash from the vault and add a spend to our transaction builder.
// We pay cash to the underwriter's policy key.
val (_, cashSigningKeys) = Cash.generateSpend(serviceHub, utx, premium, underwriter)
check(cashSigningKeys == cashSigningKeys){
throw FlowException("")
}
// Step 5. Sign the transaction.
progressTracker.currentStep = SIGNING
val ptx = serviceHub.signInitialTransaction(utx, policy.client.owningKey)
// Step 6. Get the counter-party signature.
progressTracker.currentStep = COLLECTING
val otherpartySession = initiateFlow(underwriter)
val stx = subFlow(CollectSignaturesFlow(
ptx,
listOf(otherpartySession),
COLLECTING.childProgressTracker())
)
// Step 7. Finalize the transaction.
progressTracker.currentStep = FINALIZING
return subFlow(FinalityFlow(stx, FINALIZING.childProgressTracker()))
}
}
// Allows counterparty to respond.
#InitiatedBy(Initiator::class)
class IssuePolicyResponder(val otherPartySession: FlowSession) : FlowLogic<Unit>() {
#Suspendable
override fun call() {
val signTransactionFlow = object : SignTransactionFlow(otherPartySession, SignTransactionFlow.tracker()) {
override fun checkTransaction(stx: SignedTransaction) = requireThat {
}
}
subFlow(signTransactionFlow)
}
}
}
Policy State Definition:
//Policy Class, includes premium, claim, client, underwriter, flight, and policyID
data class Policy(val premium: Amount<Currency>,
val claim: Amount<Currency>,
val client: Party,
val underwriter: Party,
val flight: String,
override val linearId: UniqueIdentifier = UniqueIdentifier()) : LinearState {
//Get clients and underwriters
override val participants: List<Party> get() = listOf(client, underwriter)
//Functions to update policy parameters
fun payPremium(amountToPay: Amount<Currency>) = copy(premium = premium + amountToPay)
fun payClaim(amountToPay: Amount<Currency>) = copy(claim = claim + amountToPay)
fun withNewClient(newClient: Party) = copy(client = newClient)
fun withNewUnderwriter(newUnderwriter: Party) = copy(underwriter = newUnderwriter)
fun withNewFlight(newFlight: String) = copy(flight = newFlight)
//Provides response
override fun toString(): String {
val clientString = (client as? Party)?.name?.organisation ?: client.owningKey.toBase58String()
val underwriterString = (underwriter as? Party)?.name?.organisation ?: underwriter.owningKey.toBase58String()
return "Policy($linearId): $clientString has paid a premium of $$premium for flight $flight, underwritten by $underwriterString with" +
"a claim amount of $$claim."
}
}
Commands in the Contract:
interface Commands : CommandData {
class Issue : TypeOnlyCommandData(), Commands
class Settle : TypeOnlyCommandData(), Commands
}
override fun verify(tx: LedgerTransaction): Unit {
val command = tx.commands.requireSingleCommand<Commands>()
val setOfSigners = command.signers.toSet()
when (command.value) {
is Commands.Issue -> verifyIssue(tx, setOfSigners)
is Commands.Settle -> verifySettle(tx, setOfSigners)
else -> throw IllegalArgumentException("Unrecognized command. You can only issue or settle.")
}
}
Not all the cash in your node's vault will be owned by your node's main public key. This is because when Cash.generateSpend generates change outputs, this change is assigned to a new confidential identity instead of your node's main identity for privacy reasons.
This is the purpose of the cashSigningKeys returned by Cash.generateSpend. It's a list of the public keys that own all the cash added to the transaction builder by Cash.generateSpend.
I'm guessing that the first time you run IssuePolicy/PayoutPolicy, it generates some cash owned by a new confidential identity. You never sign the transaction with the key of the new confidential identity. Effectively, it is this confidential identity's session that is missing when you call CollectSignaturesFlow.
Of course, it doesn't make sense to create a session with this confidential identity, since it actually corresponds to the node running the flow. Instead, you need to take the cashSigningKeys returned by Cash.generateSpend, and sign the transaction with these as well before calling CollectSignaturesFlow, by calling:
val ptx = serviceHub.signInitialTransaction(
utx,
cashSigningKeys + policy.client.owningKey)

Resources