Skip to content
This repository has been archived by the owner on Sep 7, 2023. It is now read-only.

pass arguments to contract init #789

Closed
discochance opened this issue Dec 14, 2020 · 5 comments
Closed

pass arguments to contract init #789

discochance opened this issue Dec 14, 2020 · 5 comments

Comments

@discochance
Copy link

Describe the bug
When sending arguments to the init function of a contract (see accordproject/template-archive#604) the following error is produced:

node:internal/process/promises:225
          triggerUncaughtException(err, true /* fromPromise */);
          ^
TypeError: Cannot use 'in' operator to search for '$class' in null
    at unbrand (vm.js:547:22)
    at orgXaccordprojectXhelloworldXHelloWorld.init (vm.js:84:109)
    at vm.js:3:30
    at Script.runInContext (node:vm:143:18)
    at VM.run (/local/app/node_modules/vm2/lib/main.js:211:72)
    at VMEngine.runVMScriptCall (/local/app/node_modules/@accordproject/ergo-engine/lib/vmengine.js:80:19)
    at VMEngine.invoke (/local/app/node_modules/@accordproject/ergo-engine/lib/engine.js:185:29)
    at VMEngine.init (/local/app/node_modules/@accordproject/ergo-engine/lib/engine.js:216:21)
    at Engine.init (/local/app/node_modules/@accordproject/cicero-engine/lib/engine.js:85:32)
    at test (/local/app/app.js:51:31)

The ergo code for the init function (the error occurs for both params: initRequest and request: initRequest):

// initRequest is defined in model.cto 
 clause init(params : initRequest) : MyResponse {
    set state MyState{
      stateId: "0",
      test: params.test
    };
   	return MyResponse{ output: "Done"}
  }

To Reproduce
Steps to reproduce the behavior:

  1. Use the example contract from Expose the request to cicero-engine.init() template-archive#604
  2. Init the contract with params by using result = await engine.getErgoEngine().init(engine.logicManager, clauseId ,contract, request, date,null) where request is an object of type initRequest.
  3. Observe the error

Expected behavior
The init clause should be executed properly

Additional context
For debugging purpose I printed the script and context in https://github.com/accordproject/ergo/blob/master/packages/ergo-engine/lib/vmengine.js#L67, which resulted in following output:

context = {
  data: {
    '$class': 'org.accordproject.helloworld.HelloWorldClause',
    name: 'Fred Blogs',
    clauseId: '771b899e-4be8-4aee-9ad3-1c7be6ccc3a9'
  },
  state: {
    '$class': 'org.accordproject.cicero.contract.AccordContractState',
    stateId: 'org.accordproject.cicero.contract.AccordContractState#1'
  },
  params: { '$class': 'org.accordproject.helloworld.initRequest', test: 'abc' }
}
script:
... [truncated]...
  init(context) {
    var vparams = deref(context, "params");
    var vX__contract = deref(context, "__contract");
    var vX__emit = deref(context, "__emit");
    var vX__state = deref(context, "__state");
    var vX__0 = vX__state;
    var vX__lemit = vX__emit;
    var vX__this = vX__contract;
    var vX__lstate = brand(["org.accordproject.helloworld.MyState"],concat({"stateId": "0"}, {"test": deref(unbrand(vparams), "test")}));
    return {"left" : concat(concat({"__response": brand(["org.accordproject.helloworld.MyResponse"],{"output": "Done"})}, {"__state": vX__lstate}), {"__emit": vX__lemit})};
  }
... [truncated]...

deref(context, "__contract") results in null and unbrand(vparams) later fails for this reason.
According to this output, the params key exists in the context object prior to creating the VM but still it later does not?

@discochance
Copy link
Author

Found Problem in https://github.com/accordproject/ergo/blob/master/packages/ergo-compiler/lib/logicmanager.js#L95

The LogicManager does not pass requests on in invoke calls:

If this can be added, optional parameters could be added to engine.invoke and `engine.init

discochance added a commit to discochance/ergo that referenced this issue Mar 3, 2021
discochance added a commit to discochance/ergo that referenced this issue Mar 3, 2021
Signed-off-by: Sebastian Bach <Sebastian.Bach-p2y@rub.de>
discochance added a commit to discochance/ergo that referenced this issue Mar 3, 2021
Signed-off-by: Sebastian Bach <Sebastian.Bach-p2y@rub.de>
discochance added a commit to discochance/ergo that referenced this issue Mar 3, 2021
…oject#789

Signed-off-by: Sebastian Bach <Sebastian.Bach-p2y@rub.de>
discochance added a commit to discochance/ergo that referenced this issue Mar 3, 2021
…ordproject#789

Signed-off-by: Sebastian Bach <Sebastian.Bach-p2y@rub.de>
@jeromesimeon
Copy link
Member

jeromesimeon commented Mar 14, 2021

Sorry it took me a really long time to get back to you on this. The Ergo design is still a bit tainted by earlier JS execution model which used a dispatch on the request. Unfortunately that tends to confuse a couple of things. Notably you can write clause either as clause c(c1:T1, c2: T2) { // code } v clause c(request: T') where T' might contain c1, c2.

  1. current status is that you can write clauses with any number of parameters. You can call a specific clause using invoke with param = { c1: v1, c2: c2 } where v1 is an object of type T1 and v2 is an object of type T2
  2. When the clause has no parameters (often the case with init) you can use invoke with param = {}
  3. For clauses with a single parameter e.g., clause c1(request:R1) .. clause c2(request:R2) you can use trigger which takes a single request and dispatches to the corresponding clause.

So I think your example attempts at using invoke with the request rather than trigger. I can't be sure without seeing the code you are using but my assumption is that you should be able to call invoke on your init clause by passing:

engine.getErgoEngine().init(engine.logicManager, clauseId ,contract, { request: request }, date,null)

i.e., the fourth parameter in that call would be an object containing the Ergo parameters for the invocation which is an object with a single request field.

Hope this helps.

@jeromesimeon
Copy link
Member

Another useful pointer about this:

The invoke call in README.md looks like this: https://github.com/accordproject/ergo/tree/wasm#invoke-a-clause

$ ergo invoke --template ./tests/volumediscount --clauseName volumediscount --data ./tests/volumediscount/data.json --params ./tests/volumediscount/params.json --state ./tests/volumediscount/state.json

with a ./tests/volumediscount/params.json file containing:

   {
     "request": {
       "$class": "org.accordproject.volumediscount.VolumeDiscountRequest",
       "netAnnualChargeVolume": 10.4
     }
   }

(notice the "request" field)

@discochance
Copy link
Author

Thanks for clarifying! By setting the params I was able to achieve my goal.

Would it be possible to add an optional interface for those params to the Cicero Engine init instead of always using an empty object? (see accordproject/template-archive#604)

Or is this behaviour unwanted and one should call the init function from the Ergo Engine in those cases?

@jeromesimeon
Copy link
Member

Thanks for clarifying! By setting the params I was able to achieve my goal.

Would it be possible to add an optional interface for those params to the Cicero Engine init instead of always using an empty object? (see accordproject/cicero#604)

Or is this behaviour unwanted and one should call the init function from the Ergo Engine in those cases?

hm. Actually I think so. I can't recall on top of my head why it isn't so. I'll have to review but I'll get back to you on that other issue!

Please close this one if you feel it has been adequately answered.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants