isolated-vm icon indicating copy to clipboard operation
isolated-vm copied to clipboard

Pass async methods in Isolate

Open goddessskynsfw opened this issue 2 years ago • 6 comments

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.

goddessskynsfw avatar Sep 21 '22 15:09 goddessskynsfw

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

ppedziwiatr avatar Dec 06 '22 10:12 ppedziwiatr

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, {})

LeeAdcock avatar Dec 22 '22 16:12 LeeAdcock

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!'
})()

siddharthvp avatar May 25 '24 20:05 siddharthvp

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 avatar Jun 03 '24 05:06 rakeshar3796

My bad, Got the answer, Thanks!

rakeshar3796 avatar Jun 03 '24 06:06 rakeshar3796