Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass async methods in Isolate #322

Open
goddessskynsfw opened this issue Sep 21, 2022 · 6 comments
Open

Pass async methods in Isolate #322

goddessskynsfw opened this issue Sep 21, 2022 · 6 comments

Comments

@goddessskynsfw
Copy link

Hey! I've been trying a lot of methods to pass async functions in my isolate, but nothing works. I tried the raw method, externalcopy, reference, nothing works.

@ppedziwiatr
Copy link

ppedziwiatr commented Dec 6, 2022

Hey, not sure if that's what you're asking - but here's and example of how we're doing this in our project -

  1. https://github.com/warp-contracts/warp-contracts-plugins/blob/main/warp-contracts-plugin-ivm/src/configure-ivm.ts#L75
  2. https://github.com/warp-contracts/warp-contracts-plugins/blob/main/warp-contracts-plugin-ivm/src/configure-ivm.ts#L359
    (I had to adapt it to an existing API - that's why it it splitted into two parts. You could probably "merge" this into one).

In general I'm using sandbox.setSync to set a ivm.Reference that wraps the async call:

sandbox.setSync(
    '__host__smartweave__contracts_readContractState',
    new ivm.Reference(async function (...args) {
      // eslint-disable-next-line prefer-spread
      const result = await swGlobal.contracts.readContractState.apply(swGlobal, args);
      return new ivm.ExternalCopy(result);
    })
  );

and then - while calling it from the isolate

const result = __host__smartweave__contracts_readContractState.applySyncPromise(undefined, args, {});
return result.copy(); 

- using the applySyncPromise and copying the result.

Not even sure if that's the how it should be done - unfortunately the docs are pretty vague here...

@LeeAdcock
Copy link

I think I'm trying something similar, exposing an external slow-running async method to untrusted code within the isolate, and allowing that untrusted code it to chain additional behavior to the resolved promise. I think this is the reverse of what is in @laverdet's example from #125 . I don't have it working though, the promise callbacks within the isolate don't seem to be called.

Any help appreciated.

     // slow task running outside the isolate
     const slowTask = (delay) => new Promise(resolve => setTimeout(resolve, delay)

      const isolate = new ivm.Isolate({ memoryLimit: 8 });

     // isolate runs untrusted code that calls and uses the results of the slow running task
      const untrustedScript = isolate.compileScriptSync(`
        log('start')
        _slowTask(1000, 
          new _ivm.Reference(()=>{log('success')}),  // never called
          new _ivm.Reference(()=>{log('failed')}))     // never called
      `)

      const context = isolate.createContextSync();
      context.global.setSync('_ivm', ivm)

     // Expose the slow running task, using references for callbacks, and parameters
      context.global.setSync("_slowTask", 
        (arg: number, resolve: ivm.Reference, reject: ivm.Reference) => 
          slowTask(arg).then(
            (value) => resolve.applyIgnored(undefined, [value]), 
            (err) => reject.applyIgnored(undefined, [new ivm.ExternalCopy(err).copyInto()] )
        )
      )

      context.global.setSync('log', function(...args) {
        console.log(...args);
      });  

      untrustedScript.runIgnored(context, {})

@siddharthvp
Copy link

The following works with v4.7.2 of the library. Async function can be passed into the isolate and the awaited result can be transferred out of the isolate:

import {Isolate, Reference} from "isolated-vm";

(async function () {
    const isolate = new Isolate()

    async function myAsyncFunction() {
        return new Promise((resolve) => {
            setTimeout(() => resolve('Hello from async function in isolate!'), 10);
        });
    }

    const context = await isolate.createContext();
    const jail = context.global;
    await jail.set('myAsyncFunction', new Reference(myAsyncFunction));

    const fn = await context.eval(`
        (async function untrusted() { 
            let str = await myAsyncFunction.applySyncPromise();
            return str;
        })
    `, { reference: true })
    const value = await fn.apply(undefined, [], { result: { promise: true } })

    console.log(value) // 'Hello from async function in isolate!'
})()

@rakeshar3796
Copy link

rakeshar3796 commented Jun 3, 2024

Hey @siddharthvp i've a followup question here, i'm changing the async function here like to return an object, so

async function myAsyncFunction() {
        return new Promise((resolve) => {
            setTimeout(() => resolve({ data: 'Hello from async function in isolate!' }), 10);
        });
    }

how to get this object out of the isolate?

@rakeshar3796
Copy link

My bad, Got the answer, Thanks!

@Kikobeats
Copy link

@siddharthvp thanks for your example.

Do you think it could be possible to provide code as string? I'm playing with the code but I didn't find the way.

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

No branches or pull requests

6 participants