isolated-vm
isolated-vm copied to clipboard
Pass async methods in Isolate
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.
Hey, not sure if that's what you're asking - but here's and example of how we're doing this in our project -
- https://github.com/warp-contracts/warp-contracts-plugins/blob/main/warp-contracts-plugin-ivm/src/configure-ivm.ts#L75
- 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...
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, {})
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!'
})()
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?
My bad, Got the answer, Thanks!