I'd like to initiate a flow in Corda (v3.3) shell with
flow start IOUIssueFlow state: { newIOUState: { amount: $100 } }
(the rest of the flow parameters are cut for brevity.)
however the parsing fails with
No matching constructor found:
- [com.template.IOUState]: Could not parse as a command: Instantiation of [simple type, class com.template.IOUState] value failed for JSON
property amount due to missing (therefore NULL) value for creator
parameter amount which is a non-nullable type at [Source: UNKNOWN;
line: -1, column: -1] (through reference chain:
com.template.IOUState["amount"])
IOUIssueFlow's constructor has a single state parameter of type IOUState. IOUState's constructor starts with:
data class IOUState(val amount: Amount<Currency>,
val lender : Party,
val borrower: Party,
val paid : Amount<Currency> = Amount(0, amount.token),
override val linearId: UniqueIdentifier = UniqueIdentifier()): LinearState {...
What am I missing here?
I have the same problem and it looks like bug in jackson. I wasn't able to construct an object as parameter in Corda shell, so I had to fallback to providing all necessary "simple" parameters as input of the Flow and constructing the object inside it.
Related
I am building an RPC interface that calls cordaRPCOps.startFlowDynamic() and got the following error:
Class "class com.sun.proxy.$Proxy29" is not on the whitelist or
annotated with #CordaSerializable.
I accidentally included an extra parameter to the cordaRPCOps.startFlowDynamic() call:
FlowHandle<SignedTransaction> flowHandle = cordaRPCOps.startFlowDynamic(
FungibleTokenRedeem.class,
amount,
issuer,
observers,
queryCriteria,
changeHolder,
cordaRPCOps // !!! Extra parameter !!!
);
Here is the constructor for the Flow:
public FungibleTokenRedeem(Amount<TokenType> amount, Party issuer, List<Party> observers, QueryCriteria queryCriteria, AbstractParty changeHolder) {
this.amount = amount;
this.issuer = issuer;
this.observers = observers;
this.queryCriteria = queryCriteria;
this.changeHolder = changeHolder;
}
Normally when you have too many, too few or the wrong type of parameter to cordaRPCOps.startFlowDynamic() you get a nice error that helps you identify what you did wrong:
net.corda.core.flows.IllegalFlowLogicException: A FlowLogicRef cannot
be constructed for FlowLogic of type
com.template.flows.FungibleToken.FungibleTokenRedeem: due to ambiguous
match against the constructors
In the past when I accidentally included an extra parameter it just happened to be one that is on Corda’s whitelist (i.e. String, List<> etc.). This time the class happened to be class CordaRPCOps (copy and paste mistake) which is not on the whitelist or annotated with #CordaSerializable and this more cryptic error message was the result. I just wanted a record of it in case someone makes the same mistake.
In Corda, there is a flow which provides the functionality to send SignedTransaction to another party.
open class SendTransactionFlow(otherSide: FlowSession, stx: SignedTransaction) : DataVendingFlow(otherSide, stx)
And another flow, which sends StatesAndRefs to another party:
open class SendStateAndRefFlow(otherSideSession: FlowSession, stateAndRefs: List<StateAndRef<*>>) : DataVendingFlow(otherSideSession, stateAndRefs)
On the acceptor's side corresponding receiver flow should be invoked:
open class ReceiveTransactionFlow #JvmOverloads constructor(private val otherSideSession: FlowSession,
private val checkSufficientSignatures: Boolean = true,
private val statesToRecord: StatesToRecord = StatesToRecord.NONE) : FlowLogic<SignedTransaction>()
or
class ReceiveStateAndRefFlow<out T : ContractState>(private val otherSideSession: FlowSession) : FlowLogic<#JvmSuppressWildcards List<StateAndRef<T>>>() // which invokes **ReceiveTransactionFlow** with **StatesToRecord.NONE**
The key difference between these two acceptor flows is that ReceiveStateAndRefFlow will resolve SignedTransaction on acceptor's side, but will not record states to the counterparties vault. On the other, hand: ReceiveTransactionFlow accepts StatesToRecord property and will store states into the counterparty's vault.
Question: What is the rationale that ReceiveStateAndRefFlow doesn't accept StatesToRecord property?
Turns out there is no particular reason why StatesToRecord cannot be set but there are some tricky edge cases to deal with which may end up in CorDapp developers messing up their vault!
For example: when you force the storing of a single state in the vault the corresponding output in the same transaction will not be stored, resulting you having an unconsumed state in the vault which is actually consumed.
Normally, this wouldn't matter too much because you can't spend it anyway. However, it will result in confusing vault query behaviour.
I have defined the following interface:
open class IsBustCommand(val bustParty: Party, val isBust: Boolean)
And the following command:
interface Commands : CommandData {
class GoToDirect(party: Party, isBust: Boolean) : IsBustCommand(party, isBust), Commands
}
When I run a flow, it throws:
java.io.NotSerializableException: Constructor parameter - "party" -
doesn't refer to a property of "class
com.cordacodeclub.directAgreement.contract.DirectAgreementContract$Commands$GoToDirect"
-> class com.cordacodeclub.directAgreement.contract.DirectAgreementContract$Commands$GoToDirect
at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintForType(Schema.kt:438)
~[corda-node-api-3.3-corda.jar:?]
at net.corda.nodeapi.internal.serialization.amqp.SchemaKt.fingerprintForType$default(Schema.kt:352)
~[corda-node-api-3.3-corda.jar:?]
If I change the parameter name in the constructor to:
class GoToDirect(bustParty: Party, isBust: Boolean) : IsBustCommand(bustParty, isBust), Commands```
Then I no longer receive an exception. What is going on here?
The Corda serialisation framework requires each constructor parameter to correspond to a property of the class of the same name.
In the first example, party is not a property of GoToDirect or its super-classes/interfaces, so this condition fails (you could fix this by adding a val to the constructor as follows: class GoToDirect(val party: Party, isBust: Boolean)).
In the second example, bustParty is not a property of GoToDirect, but it is a property of IsBustCommand, so this condition is met and serialisation succeeds.
In kubectl/run.go in Kubernetes code, the Generate function has a result list of these two types:
runtime.Object, error
The last line of the function is:
return &deployment, nil
runtime is imported:
k8s.io/apimachinery/pkg/runtime
I got runtime by running go get on that import statement, and Object is defined in interfaces.go:
type Object interface {
GetObjectKind() schema.ObjectKind
DeepCopyObject() Object
}
(And I found the same code on the web here.)
The address operator creates a pointer... more specifically, the Go spec states:
For an operand x of type T, the address operation &x generates a pointer of type *T to x.
and pointers have a type distinct from their base type:
A pointer type denotes the set of all pointers to variables of a given type, called the base type of the pointer.
How does &deployment satisfy the runtime.Object type?
My best guess so far is that deployment implements the runtime.Object interface, and mapping &deployment to runtime.Object satisfies this rule of assignability:
T is an interface type and x implements T.
and that a return statement mapping to a result list type is equivalent to assignment in this respect. Is this correct? If not, is there another part of the specification or documentation that explains it?
deployment is a local variable, its declaration:
deployment := extensionsv1beta1.Deployment{
// ...
}
Where extensionsv1beta1 from the imports:
import (
// ...
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
// ...
)
Doc of extensionsv1beta1.Deployment. Its definition is:
type Deployment struct {
metav1.TypeMeta `json:",inline"`
// ...other fields...
}
It embeds metav1.TypeMeta, which has a method GetObjectKind() method with pointer receiver. This means a pointer to Deployment also has this method, because Spec: Struct types:
Given a struct type S and a defined type T, promoted methods are included in the method set of the struct as follows:
If S contains an embedded field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.
And Deployment has a "direct" DeepCopyObject() method, again with pointer receiver. So the method set of *Deployment contains this method.
And last quoting Spec: Interface types:
An interface type specifies a method set called its interface. A variable of interface type can store a value of any type with a method set that is any superset of the interface. Such a type is said to implement the interface.
So this means the method set of *Deployment has all the methods defined by Object, or in other words: the method set of *Deployment is a superset of the method set of Object, so *Deployment implements Object.
deployment is of type extensionsv1beta1.Deployment, which means &deployment is of type *extensionsv1beta1.Deployment, which we showed above that it implements Object; so the value &deployment can be assigned to or be stored in a variable of type Object.
I'm trying to use macro annotations in scala, where my macro annotation would take an argument of another type. It would then use scala reflection to look at the passed in type, and add some methods as appropriate.Eg.
trait MyTrait {
def x: Int
def y: Float
}
#MyAnnotation class MyClass //<-- somehow, this annotation should reference MyTrait
class MyAnnotation(val target: Any) extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro MyAnnotationImpl.impl
}
object MyAnnotationImpl {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
// if I can get a handle on the type MyTrait in here
// then I can call .members on it, etc.
...
}
}
Basically, the same thing as Using Scala reflection in Scala macros, except using macro annotations. However, when I try to template my macro annotation with a TypeTag
class MyAnnotation[T](val target: Any) extends StaticAnnotation {
def macroTransform[T](annottees: Any*) = macro MyAnnotationImpl.impl[T]
}
object MyAnnotationImpl {
def impl[T: c.WeakTypeTag](c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
...
}
}
I get
[error] /Users/imran/other_projs/learn_macros/macros/src/main/scala/com/imranrashid/oleander/macros/MacrosWithReflection.scala:7: macro annotation has wrong shape:
[error] required: def macroTransform(annottees: Any*) = macro ...
[error] found : def macroTransform[T](annottees: Any*) = macro ...
[error] class MyAnnotation[T](val target: Any) extends StaticAnnotation {
[error] ^
I've also tried to make the type an argument to my annotation, so I would use it like #MyAnnotation(MyTrait) class Foo .... I can extract the name as a String with something like
val targetTrait = c.prefix.tree match {
case Apply(Select(New(Ident(_)), nme.CONSTRUCTOR), List(Ident(termName))) => termName
}
but, I'm not sure what I can do w/ that String to get back the full type. I've also tried variants like #MyAnnotation(typeOf[MyTrait]) class Foo ..., and then use c.eval on the typeOf inside my macro, but that doesn't compile either.
In macro paradise 2.0.0-SNAPSHOT we have quite a tricky way of accessing type parameters for macro annotations (the situation will improve later on when we have dedicated APIs for that, but right now it's very difficult to introduce new functionality to scala-reflect.jar in macro paradise, so the current API is a bit rough).
For now it's necessary to specify the type parameter on the annotation class and not to declare any type parameters on the macroTransform method. Then, in macro expansion, access c.macroApplication and extract the untyped tree corresponding to the passed type parameter. Afterwards, do c.typeCheck as described in Can't access Parent's Members while dealing with Macro Annotations.
As Eugene points out in his answer it is possible to match on the tree of the whole macro application. Like every Scala method, annotation macro applications can take multiple type argument lists as well as multiple value argument lists.
Consider the macro application of an annotation macro called test:
#test[A, B][C, D](a, b)(c, d) trait Foo
In the implementation of test we can inspect the macro application by
println(show(c.macroApplication))
which will result in:
new test[A, B][C, D](a, b)(c, d).macroTransform(abstract trait Foo extends scala.AnyRef)
To extract the (type/value) parameters from the tree you have to pattern match on the tree. A parser for an arbitrary amount of parameter lists can be found in this project
Using this parser retrieving the first value argument of the macro application is as easy as
val List(List(arg)) = MacroApp(c.macroApplication).termArgs