IllegalFlowLogicException when calling CordaRPCOps.startFlow - corda

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

Related

Getting an error unresolved refrence in corda contract?

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

kotlin lambda passed as argument to corda (v3.2) flow throws kryo serialization exception

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!

FlowJs - class is incompatible with interface it implements while used in array

I come from C# background and trying to do the thing that would work there, but for some reason does not work with flow js.
I have interface and a class that implements interface:
interface Id {
id: number
}
class A implements Id {
id: number
}
When I try to use object of a class in place of interface this way, everything works:
var a = new A()
function f(v: Id) {}
f(a)
But when I try to use array of objects, it gives me an error:
var array: Array<A> = [ new A(), new A() ]
function f(array: Array<Id>){}
f(array)
Error is:
'Id (This type is incompatible with A)'
Why is that?

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.

Kotlin bound callable references inconsistency

Today I was creating unit tests for my Presenter in Android app and I noticed some inconsistency with bound callable references. Is it under development or is it language bug? I found that in kotlin 1.1 bound callable references are supported. But my code fails with kotlin 1.1.2-4.
In my presenter class, tested method reads data from database and dao.getAllItems() method have no parameters while view takes list --> view.showData(List<Item>).
I'm using Mockito, RxJava2 and Room Persistance library.
class ItemsPresenter #Inject constructor(private val itemDao: ItemDao) : Presenter<ItemsView>
{
val TAG = this.javaClass.name!!
private val disposables: CompositeDisposable = CompositeDisposable()
private lateinit var view: ItemsView
override fun onCreate(view: ItemsView)
{
this.view = view
}
override fun onDestroy()
{
disposables.clear()
}
fun onGetItems()
{
Observable.fromCallable(itemDao::getAllItems)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ data -> view.showData(data) },
{ throwable -> view.showLoadingDataError(throwable.localizedMessage) }
)
}
}
I have created test for onGetItems() method
#RunWith(KotlinTestRunner::class)
class ItemsPresenterTest
{
private lateinit var view: ItemsView
private lateinit var dao: ItemDao
private lateinit var presenter: ItemsPresenter
#Before
fun setup()
{
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.io() }
dao = mock(ItemDao::class.java)
view = mock(ItemsView::class.java)
presenter = ItemsPresenter(dao)
}
#Test
fun onGetItemsTest()
{
val list = ArrayList<Item>()
When(dao.getAllItems()).thenReturn(list)
presenter.onCreate(view)
presenter.onGetItems()
verify(dao).getAllItems()
verify(view).showData(list)
}
}
When I have setup as above, the test passes without problems. But when I change line
{ data -> view.showData(data) }
to
{ view::showData }
Then my test fails saying
Wanted but not invoked:
itemsView.showData([]);
Is it language bug? Because vulnerable code compiles fine and runs, it just causes the method to not be invoked at all, without any errors.
To clarify, the same code written in Java 8 works fine, lambda argument is correctly passed into method reference. As you can see in Kotlin bound callable references work fine when used with methods that takes no parameters, otherwise they are not called at all.
You should change
{ data -> view.showData(data) }
to
(view::showData)
to pass in the method reference correctly. This way, with (), you're passing in the method reference as the parameter of the subscribe method.
Using {}, you define a new function with a lambda to be given to the subscribe method.
Writing down
{ view::showData }
is equivalent to
{ it -> view::showData }
which is a function that ignores its parameter, and returns the method reference view::showData.

Resources