Error: Script startup exceeded CPU time limit. [co...
# workers-help
s
When I static import some wasm libraries, I often get the error
Error: Script startup exceeded CPU time limit. [code: 10021] when using static import
when deploying. It is resolved when I use dynamic import (
import('...
), but dynamic import apparently doesn't work with wasm. Any suggestions?
Specifically this code:
Copy code
js
import { decodeAddress, encodeAddress } from '@polkadot/keyring';
import { hexToU8a, isHex } from '@polkadot/util';

// Checks whether provided wallet string is a valid address (from https://polkadot.js.org/docs/util-crypto/examples/validate-address/)
export async function validate(wallet: string): Promise<boolean>{
  try {
    encodeAddress(
      isHex(wallet) ? hexToU8a(wallet) : decodeAddress(wallet)
    );
  } catch (e) {
    return false;
  }
  return true;
};
i
What error?
s
Error in the title - edited for clarity
i
So this means that something in your Worker (likely either
@polkadot/keyring
or
@polkadot/util
) is doing work in the global scope - outside of a
fetch
handler.
There's no way to resolve this aside from not doing work in the global scope, which might mean you have to change which libraries you are using.
s
OK. I tried to remedy that by dynamically loading them, but I read somewhere (I can't find it now) that wasm can't be dynamically loaded - is that true?
*can't be dynamically loaded in cloudflare workers
i
You can load WASM by doing `WebAssembly.instantiate() `inside your
fetch
handler, yeah. https://developers.cloudflare.com/workers/platform/web-assembly/
Ignore the "This should be done at the top level of the script to avoid instantiation on every request." part - if you tried to do it at the top level of the script you would still get this error.
s
Digging a bit deeper into the compiled js when I dynamically import them with:
Copy code
js
// Checks whether provided wallet string is a valid address (from https://polkadot.js.org/docs/util-crypto/examples/validate-address/)
export async function validate(wallet: string): Promise<boolean>{
  try {
    const polk_keyring = await import('@polkadot/keyring');
    const polk_util = await import('@polkadot/util');
    
    polk_keyring.encodeAddress(
      polk_util.isHex(wallet) ? polk_util.hexToU8a(wallet) : polk_keyring.decodeAddress(wallet)
    );
  } catch (e) {
    return false;
  }
  return true;
};
I get this error when using the function:
Copy code
FATAL: Unable to initialize @polkadot/wasm-crypto:: WebAssembly.instantiate(): Wasm code generation disallowed by embedder
The compiled index.js does use Webassembly.instantiate:
Copy code
js
    try {
      if (!wasmBytes2 || !wasmBytes2.length) {
        throw new Error("No WebAssembly provided for initialization");
      } else if (typeof WebAssembly !== "object" || typeof WebAssembly.instantiate !== "function") {
        throw new Error("WebAssembly is not available in your environment");
      }
      const source = await WebAssembly.instantiate(wasmBytes2, {
        wbg
      });
...
I found the source that said you can't dynamically load wasm - but maybe I'm misinterpreting it? https://github.com/cloudflare/workers-sdk/issues/1366#issuecomment-1172255671 He says "Specifically, you can't initialise an instance from a string like above, it has to be a separate modules that's imported." - I don't know what he's referring to by 'like above' so maybe I'm wrong.
Ah I think I understand - I can't instantiate a string object - which it sees as generating the wasm on the fly - but I can instantiate a local
.wasm
file. Is that the difference, and why my dynamic loading is failing?
i
Yeah, exactly.
Workers can't instantiate from a string, only the WASM file
s
Thanks for confirming! One more question then - when it's statically loaded in the global context, what's different? Are there different rules? I am a javascript/typescript noob, so thank you.
i
The difference in this case is related to Workers. Workers prohibit doing a lot of work in the global context to avoid problems with cold starts and to allow other startup optimizations.
It's not related to JS, JS fully allows you to do that.
s
The global context allows polkadot to instantiate a string, whereas outside the global context it fails. So it seems it's less restrictive in the global context (except for being under 200ms).
i
What error do you get inside the fetch context?
s
I should have said, inside the fetch context it fails. The error is
FATAL: Unable to initialize @polkadot/wasm-crypto:: WebAssembly.instantiate(): Wasm code generation disallowed by embedder
, which after our discussion makes sense. I'm just not sure why a static/global include (as shown in my second comment in this thread) DOES work (but times out, which I do understand why).
i
Interesting. I'm unsure why as well.. it might be that if the global scope were to not time out it would also raise that error
s
Sometimes it doesn't time out, and works just fine. I could just keep deploying until it doesn't time out, which I've done in the past, but I trying to find out if I can fix this, while also learning a lot about typescript.
Thanks for your help. I'll keep digging around.
I resolved it twice - dynamic loading still worked, despite the error. It seems the polkadot library falls back to js implementation if it can't use wasm. Ultimately, I am going with a much lighter js library ('@subsquid/ss58-codec') that provides the function I need at a much smaller size, with the tradeof of requiring
node_compat=true
.