fix(wasm): unify error handling for mm2_main
There was a significant inconsistency in how startup failures are handled between the native and WASM implementations of mm2_main:
- In the native implementation, startup failures return appropriate error codes as
i8values - In the wasm implementation, startup failures are only logged but not propagated back to JavaScript
This inconsistency made it difficult for web GUI to reliably detect when mm2_main failed to start, which is particularly problematic when users were trying to log in. While native GUI can get immediate feedback on failure, web GUI had to use timeouts or other workarounds.
Changes
This PR fixes the inconsistency by:
- Making
mm2_mainin wasm return a Promise (async fnwithResult<i8, JsValue>) - Creating a unified
StartupErrorCodeenum shared between native and WASM
pub enum StartupErrorCode {
/// Operation completed successfully
Ok = 0,
/// Invalid parameters were provided to the function
InvalidParams = 1,
/// The configuration was invalid (missing required fields, etc.)
ConfigError = 2,
/// MM2 is already running
AlreadyRunning = 3,
/// MM2 initialization failed
InitError = 4,
/// Failed to spawn the MM2 process/thread
SpawnError = 5,
}
- Introducing a structured
StartupErrortype with both code and descriptive message for the wasm implementation, allowing JavaScript to receive detailed error information. - Properly propagating errors from
lp_mainas Promise rejections in the wasm implementation instead of just logging them.
Architecture Improvement
As part of this fix, we've separated the previously combined initialization and runtime functionality into two distinct phases:
lp_main: Handles initialization and configuration, returns ctx when successfullp_run: Takes ctx fromlp_mainand manages runtime execution
This separation provides cleaner error boundaries, letting us properly propagate initialization errors before moving to the runtime phase. In the wasm implementation, we now resolve the Promise when initialization succeeds and spawn the runtime as a separate task, allowing JavaScript to immediately receive startup success/failure feedback.
This change not only fixes the error reporting inconsistency but also improves the overall architecture by better separating concerns between initialization and runtime execution.
fixes https://github.com/KomodoPlatform/komodo-defi-framework/issues/2383
Would it be a significant request to make the native function async with similar behaviour to the wasm build? Is that even possible since it's an exported C function?
I don't think that is possible, but I will look into it.
Alternatively, we could add an optional log callback argument to mm2_main, but this might not be ideal for you since it means an inconsistent mm2_main signature between native vs wasm.
Will look into that and other alternatives. But I will do that in a different PR after this is reviewed and merged by @KomodoPlatform/mm2 team.
@shamardy I mentioned in my bug report a suggestion about adding a dedicated SSE for the KDF lifecycle, but this may be overly complicated, and a simple, more sustainable solution is to have a callback parameter in mm2_main for the same data.
I've tested to trigger a launch fails, which returned exit code 1: InvalidParams in both wasm and cli for the following cases (cli logs for reference):
08 10:39:37, mm2:315] mm2:388] Password can't contain the word password08 10:41:55, mm2:315] mm2:345] Couldn't parse mm2 config to JSON format!08 10:45:56, mm2:315] mm2:388] mm2:170] lp_native_dex:435] lp_native_dex:629] Error deserializing 'seednodes' config field: invalid type: string "46.4.87.18", expected a sequence
I was unable to trigger exit code 2: ConfigError, even with nothing but {} in my MM2.json
When attempting to run a second instance of ./kdf with the same MM2.json file, I got error code: 101, not the expected Error code 3: AlreadyRunning.
Unsure how to trigger the other errors, let me know if it is simple enough. I recall there used to be a simulate_panic rpc - does this still exist? or did I dream it? If not, might be useful to mimic launch fails with force_code param.
I've tested to trigger a launch fails, which returned exit code 1: InvalidParams in both wasm and cli for the following cases (cli logs for reference):
08 10:39:37, mm2:315] mm2:388] Password can't contain the word password 08 10:41:55, mm2:315] mm2:345] Couldn't parse mm2 config to JSON format! 08 10:45:56, mm2:315] mm2:388] mm2:170] lp_native_dex:435] lp_native_dex:629] Error deserializing 'seednodes' config field: invalid type: string "46.4.87.18", expected a sequence
Was this using mm2_main from kdf as a lib or from starting the binary? This PR is for mm2_main provided by kdf as a lib.
I was unable to trigger exit code 2: ConfigError, even with nothing but {} in my MM2.json
ConfigError can be triggered when coins is null in wasm, or if config is not json in native. I believe If I did this https://github.com/KomodoPlatform/komodo-defi-framework/pull/2389#discussion_r2000909207 which might be a big change, it can result in totally different error variants.
When attempting to run a second instance of ./kdf with the same MM2.json file, I got error code: 101, not the expected Error code 3: AlreadyRunning.
Can you provide more info on this, I want to know whereerror code: 101 originated from.
Unsure how to trigger the other errors, let me know if it is simple enough.
for InitError, lp_main has to fail, it should have been triggered by the password case for instance. Did you try it in wasm?
I recall there used to be a simulate_panic rpc - does this still exist? or did I dream it? If not, might be useful to mimic launch fails with force_code param.
simulate_panic was removed here https://github.com/KomodoPlatform/komodo-defi-framework/pull/2270 , I don't think this actually tested any real word scenario of actually running mm2/kdf.
Edit: Maybe fixing this https://github.com/KomodoPlatform/komodo-defi-framework/pull/2389#discussion_r2000909207 and propgating errors back to caller will fix your issues @smk762 as I see we log only the errors of lp_main. But for wasm it should have worked.
can you please try again @smk762 for native, if the issue was native only. ~~I also added a new config parameter called startup_timeout that defaults to 60 seconds, this parameter controls how long the main thread will wait for KDF initialization to complete before returning an error. This is an indirect necessity to propagating back initialization errors when they happen like the password one.~~
Edit: startup_timeout is removed ref. https://github.com/KomodoPlatform/komodo-defi-framework/pull/2389#discussion_r2039026673
Was this using mm2_main from kdf as a lib or from starting the binary?
it was from binary. I'll setup to retest this as lib