Getting an error unresolved refrence in corda contract? - corda

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>()

Related

How to pass secure hash to transaction builder

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.

Find a state by unique ID in a cordapp api/flow

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
}
}

IllegalFlowLogicException when calling CordaRPCOps.startFlow

In a CorDapp, I defined the following flow:
#InitiatingFlow
#StartableByRPC
class EchoFlow(private val msg: String): FlowLogic<String>() {
override fun call(): String {
return msg
}
}
I then deployed the CorDapp to a node and tried to run it as follows:
val result = proxy.startFlow { EchoFlow("msg") }.returnValue.getOrThrow()
However, I received the following exception:
Exception in thread "main" net.corda.core.flows.IllegalFlowLogicException:
FlowLogicRef cannot be constructed for FlowLogic of type com.template.EchoFlow due to missing constructor for arguments: []
How should I invoke a flow using CordaRPCOps.startFlow?
CordaRPCOps.startFlow is used by passing a flow constructor, following by a vararg of arguments to the flow. So in Kotlin, you'd invoke the flow by running:
val result = proxy.startFlow(::EchoFlow, "msg").returnValue.getOrThrow()
Where ::ClassName is the Kotlin syntax for getting a reference to the flow's constructor.
Alternatively, you could use CordaRPCOps.startFlowDynamic. This method takes a flow instance instead, as follows:
val result = proxy.startFlowDynamic(EchoFlow("msg")).returnValue.getOrThrow()

How can I set a property of a companion object in Kotlin via reflection?

When I have a class that has a companion object, is it possible to set a property in this companion object using reflection? I can do it with normal properties, but fail on companion objects:
import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.memberProperties
class WithProperty {
lateinit var prop: String
companion object {
lateinit var companionProp: String
}
fun test() = "$companionProp $prop"
}
fun main(args: Array<String>) {
val obj = WithProperty()
val prop = obj::class.memberProperties.filter { it.name == "prop" }.first()
if (prop is KMutableProperty<*>) {
prop.setter.call(obj, "world")
}
val companion = obj::class.companionObject
if (companion != null) {
val companionProp = companion.memberProperties.filter { it.name == "companionProp" }.first()
if (companionProp is KMutableProperty<*>) {
companionProp.setter.call(companionProp, "hello") // <-- what must go here as first argument?
}
}
println(obj.test())
}
Calling the setter for the normal property works as it should, but when I call companionProp.setter.call(companionProp, "hello") I get
Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
What do I have to pass as first argument to call() to succeed?
Edit: I wrote companionPropas first argument, but that definitely is wrong, I actually tried with the companion object, but that is not working as well.
object is not an instance of declaring class
Just as in Java, you need to pass the object itself as the first parameter when calling reflective methods.
The first parameter to call should be the companion object, as that is the object whose property you are trying to modify.
You are passing the companion's class object instead of the companion object itself.
A companion object is accessible either via ClassName.Companion, or when using further reflection, through KClass#companionObjectInstance.
companionProp.setter.call(WithProperty.Companion, "hello")
companionProp.setter.call(obj::class.companionObjectInstance, "hello")
companionProp.setter.call(WithProperty::class.companionObjectInstance, "hello")
When run, both variants print hello world as intended.
Keep in mind that Foo.Companion will result in a compile error if the companion object does not exist while the reflective variant will return null instead.
the first argument is the instance of the declaring class.
you pass a KProperty instance companionProp rather than a companion object instance. However, you can using KClass.companionObjectInstance to obtain the compantion instance. for example:
//a non-static property having a receiver, so it should be a KMutableProperty1 here
// v
if (companionProp is KMutableProperty1<*, *>) {
// get the companion object instance ---v
companionProp.setter.call(obj::class.companionObjectInstance, "hello")
}
You can solve the same problem using java reflection.
Companion class:
class Example {
companion object {
val EXAMPLE_VALUE = "initial"
}
}
Update a property using java reflection:
val field = Example::class.java.getDeclaredField("EXAMPLE_VALUE")
field.isAccessible = true
field.set(null, "replaced")
Tested with Kotlin 1.5.30 on Android 12:
Log.d("test-companion", Example.EXAMPLE_VALUE) // outputs "replaced"
WARNING: I'm not sure if java reflection is reliable for this case. It assumes some implementation details of Kotlin complier which could change in a future version. But the solution should be fine for a quick workaround. I used it to verify a bug fix on customer side before the next release of my library.

How to define one unshared fact in Corda

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.

Resources