-
Notifications
You must be signed in to change notification settings - Fork 6
Post-review follow-up #246
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
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
5bc02e3
Post-review follow-up
ernest-nowacki 644632f
Revert the hard cap
ernest-nowacki 420783a
Merge branch 'main' of github.com:smartcontractkit/cre-sdk-typescript…
ernest-nowacki 5afd043
Merge branch 'main' of github.com:smartcontractkit/cre-sdk-typescript…
ernest-nowacki 8de74be
Improvements
ernest-nowacki 0bd1522
no need to commit cargo lock for this
ernest-nowacki dc146d2
Merge branch 'main' of github.com:smartcontractkit/cre-sdk-typescript…
ernest-nowacki e4fb93a
CR follow ups
ernest-nowacki File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| node_modules | ||
| src/javy_chainlink_sdk/target/ | ||
| src/cre_wasm_exports/target/ | ||
| .cargo-target/ | ||
| .turbo | ||
|
|
||
|
|
||
Binary file modified
BIN
-1.12 KB
(100%)
packages/cre-sdk-javy-plugin/dist/javy-chainlink-sdk.plugin.wasm
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,22 +1,22 @@ | ||
| use cre_wasm_exports::{__clear_registry, extend_wasm_exports}; | ||
| use javy_plugin_api::{ | ||
| import_namespace, | ||
| Config, import_namespace, | ||
| javy::{Runtime, quickjs::prelude::*}, | ||
| Config, | ||
| }; | ||
|
|
||
| use base64::Engine; | ||
| use javy_plugin_api::javy::quickjs::{ | ||
| ArrayBuffer, Ctx, Error, Exception, FromJs, TypedArray, Value, | ||
| }; | ||
| use rand::{Rng, SeedableRng}; | ||
| use rand_chacha::ChaCha8Rng; | ||
| use std::collections::HashMap; | ||
| use std::env; | ||
| use std::sync::{Mutex, OnceLock}; | ||
| use base64::Engine; | ||
| use rand::{Rng, SeedableRng}; | ||
| use rand_chacha::ChaCha8Rng; | ||
|
|
||
| static CURRENT_MODE: Mutex<i32> = Mutex::new(0); | ||
| static RANDOM_GENERATORS: OnceLock<Mutex<HashMap<i32, ChaCha8Rng>>> = OnceLock::new(); | ||
| const MAX_RESPONSE_LEN_BYTES: i32 = 64 * 1024 * 1024; | ||
|
|
||
| // ✅ Host imports: implemented in Go | ||
| #[link(wasm_import_module = "env")] | ||
|
|
@@ -96,8 +96,63 @@ pub fn config() -> Config { | |
| config | ||
| } | ||
|
|
||
| /// Applies CRE plugin globals and host bindings. Used by the default plugin build and by generated host crates that add `--cre-exports` extensions. | ||
| /// | ||
| fn validate_max_response_len(max_len: i32) -> Result<usize, &'static str> { | ||
| if max_len < 0 { | ||
| return Err("maxLen < 0"); | ||
| } | ||
|
|
||
| if max_len > MAX_RESPONSE_LEN_BYTES { | ||
| return Err("maxLen exceeds maximum allowed response size"); | ||
| } | ||
|
|
||
| Ok(max_len as usize) | ||
| } | ||
|
|
||
| fn checked_response_buffer_len(ctx: &Ctx<'_>, max_len: i32) -> Result<usize, Error> { | ||
| validate_max_response_len(max_len).map_err(|message| Exception::throw_range(ctx, message)) | ||
| } | ||
|
|
||
| /// Invokes a host fn with shape `(req_ptr, req_len, buf_ptr, max_len) -> i64`, | ||
| /// validates `max_len`, returns the populated response slice or maps host errors | ||
| /// to JS exceptions. `op_name` appears in the empty-error fallback; `capacity_msg` | ||
| /// is static because `Error::new_into_js` requires `&'static str`. | ||
| fn dispatch_host_call( | ||
| ctx: &Ctx<'_>, | ||
| req: ArgBytes, | ||
| max_len: i32, | ||
| op_name: &str, | ||
| capacity_msg: &'static str, | ||
| host_fn: unsafe extern "C" fn(*const u8, i32, *mut u8, i32) -> i64, | ||
| ) -> Result<Vec<u8>, Error> { | ||
| let max_len_usize = checked_response_buffer_len(ctx, max_len)?; | ||
| let req_bytes = req.0; | ||
| let mut buf = vec![0u8; max_len_usize]; | ||
|
|
||
| let n = unsafe { | ||
| host_fn( | ||
| req_bytes.as_ptr(), | ||
| req_bytes.len() as i32, | ||
| buf.as_mut_ptr(), | ||
| max_len, | ||
| ) | ||
| }; | ||
| if n < 0 { | ||
| let error_len = (-n) as usize; | ||
| let error_msg = | ||
| String::from_utf8_lossy(&buf[..error_len.min(max_len_usize)]).into_owned(); | ||
| let error_msg = if error_msg.is_empty() { | ||
| format!("{op_name} failed") | ||
| } else { | ||
| error_msg | ||
| }; | ||
| return Err(Exception::throw_message(ctx, &error_msg)); | ||
| } | ||
| if n > max_len_usize as i64 { | ||
| return Err(Error::new_into_js("Error", capacity_msg)); | ||
| } | ||
| Ok(buf[..n as usize].to_vec()) | ||
| } | ||
|
|
||
| /// Duplicate export names are caught eagerly by `extend_wasm_exports`. | ||
| pub fn modify_runtime(runtime: Runtime) -> Runtime { | ||
| __clear_registry(); | ||
|
|
@@ -118,118 +173,44 @@ pub fn modify_runtime(runtime: Runtime) -> Runtime { | |
| &ctx, | ||
| "awaitCapabilities", | ||
| Func::from(|ctx: Ctx<'_>, req: ArgBytes, max_len: i32| { | ||
| if max_len < 0 { | ||
| return Err(Exception::throw_range(&ctx, "maxLen < 0")); | ||
| } | ||
| let req_bytes = req.0; | ||
| let mut buf = vec![0u8; max_len as usize]; | ||
|
|
||
| let n = unsafe { | ||
| await_capabilities( | ||
| req_bytes.as_ptr(), | ||
| req_bytes.len() as i32, | ||
| buf.as_mut_ptr(), | ||
| max_len, | ||
| ) | ||
| }; | ||
| if n < 0 { | ||
| let error_len = (-n) as usize; | ||
| let error_msg = | ||
| String::from_utf8_lossy(&buf[..error_len.min(max_len as usize)]).into_owned(); | ||
| let error_msg_static: &'static str = Box::leak(error_msg.into_boxed_str()); | ||
| return Err(Error::new_into_js("Error", error_msg_static)); | ||
| } | ||
| if n > max_len as i64 { | ||
| return Err(Error::new_into_js( | ||
| "Error", | ||
| "await_capabilities: host returned length exceeding buffer capacity", | ||
| )); | ||
| } | ||
|
|
||
| let out = &buf[..n as usize]; | ||
| Ok::<Vec<u8>, Error>(out.to_vec()) | ||
| dispatch_host_call( | ||
| &ctx, | ||
| req, | ||
| max_len, | ||
| "await_capabilities", | ||
| "await_capabilities: host returned length exceeding buffer capacity", | ||
| await_capabilities, | ||
| ) | ||
| }), | ||
| ); | ||
|
|
||
| extend_wasm_exports( | ||
| &ctx, | ||
| "getSecrets", | ||
| Func::from(|ctx: Ctx<'_>, req: ArgBytes, max_len: i32| { | ||
| if max_len < 0 { | ||
| return Err(Exception::throw_range(&ctx, "maxLen < 0")); | ||
| } | ||
| let req_bytes = req.0; | ||
| let mut buf = vec![0u8; max_len as usize]; | ||
|
|
||
| let n = unsafe { | ||
| get_secrets( | ||
| req_bytes.as_ptr(), | ||
| req_bytes.len() as i32, | ||
| buf.as_mut_ptr(), | ||
| max_len, | ||
| ) | ||
| }; | ||
| if n < 0 { | ||
| let error_len = (-n) as usize; | ||
| let error_msg = | ||
| String::from_utf8_lossy(&buf[..error_len.min(max_len as usize)]).into_owned(); | ||
| let error_msg = if error_msg.is_empty() { | ||
| "get_secrets failed".to_string() | ||
| } else { | ||
| error_msg | ||
| }; | ||
| return Err(Exception::throw_message(&ctx, &error_msg)); | ||
| } | ||
| if n > max_len as i64 { | ||
| return Err(Error::new_into_js( | ||
| "Error", | ||
| "get_secrets: host returned length exceeding buffer capacity", | ||
| )); | ||
| } | ||
|
|
||
| let out = &buf[..n as usize]; | ||
| Ok::<Vec<u8>, Error>(out.to_vec()) | ||
| dispatch_host_call( | ||
| &ctx, | ||
| req, | ||
| max_len, | ||
| "get_secrets", | ||
| "get_secrets: host returned length exceeding buffer capacity", | ||
| get_secrets, | ||
| ) | ||
| }), | ||
| ); | ||
|
|
||
| extend_wasm_exports( | ||
| &ctx, | ||
| "awaitSecrets", | ||
| Func::from(|ctx: Ctx<'_>, req: ArgBytes, max_len: i32| { | ||
| if max_len < 0 { | ||
| return Err(Exception::throw_range(&ctx, "maxLen < 0")); | ||
| } | ||
| let req_bytes = req.0; | ||
| let mut buf = vec![0u8; max_len as usize]; | ||
|
|
||
| let n = unsafe { | ||
| await_secrets( | ||
| req_bytes.as_ptr(), | ||
| req_bytes.len() as i32, | ||
| buf.as_mut_ptr(), | ||
| max_len, | ||
| ) | ||
| }; | ||
| if n < 0 { | ||
| let error_len = (-n) as usize; | ||
| let error_msg = | ||
| String::from_utf8_lossy(&buf[..error_len.min(max_len as usize)]).into_owned(); | ||
| let error_msg = if error_msg.is_empty() { | ||
| "await_secrets failed".to_string() | ||
| } else { | ||
| error_msg | ||
| }; | ||
| return Err(Exception::throw_message(&ctx, &error_msg)); | ||
| } | ||
| if n > max_len as i64 { | ||
| return Err(Error::new_into_js( | ||
| "Error", | ||
| "await_secrets: host returned length exceeding buffer capacity", | ||
| )); | ||
| } | ||
|
|
||
| let out = &buf[..n as usize]; | ||
| Ok::<Vec<u8>, Error>(out.to_vec()) | ||
| dispatch_host_call( | ||
| &ctx, | ||
| req, | ||
| max_len, | ||
| "await_secrets", | ||
| "await_secrets: host returned length exceeding buffer capacity", | ||
| await_secrets, | ||
| ) | ||
| }), | ||
| ); | ||
|
|
||
|
|
@@ -312,9 +293,8 @@ pub fn modify_runtime(runtime: Runtime) -> Runtime { | |
| "getWasiArgs", | ||
| Func::from(|_ctx: Ctx<'_>| -> Result<String, Error> { | ||
| let args: Vec<String> = env::args().collect(); | ||
| let args_json = serde_json::to_string(&args).map_err(|_| { | ||
| Error::new_into_js("Error", "Failed to serialize args to JSON") | ||
| })?; | ||
| let args_json = serde_json::to_string(&args) | ||
| .map_err(|_| Error::new_into_js("Error", "Failed to serialize args to JSON"))?; | ||
| Ok(args_json) | ||
| }), | ||
| ); | ||
|
|
@@ -346,3 +326,30 @@ pub fn modify_runtime(runtime: Runtime) -> Runtime { | |
|
|
||
| runtime | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do the tests need to be in the same file? |
||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn validate_max_response_len_accepts_values_within_cap() { | ||
| assert_eq!(validate_max_response_len(0), Ok(0)); | ||
| assert_eq!( | ||
| validate_max_response_len(MAX_RESPONSE_LEN_BYTES), | ||
| Ok(MAX_RESPONSE_LEN_BYTES as usize) | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn validate_max_response_len_rejects_negative_values() { | ||
| assert_eq!(validate_max_response_len(-1), Err("maxLen < 0")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn validate_max_response_len_rejects_values_above_cap() { | ||
| assert_eq!( | ||
| validate_max_response_len(MAX_RESPONSE_LEN_BYTES + 1), | ||
| Err("maxLen exceeds maximum allowed response size") | ||
| ); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is this constant coming from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think GO have anything like this. That's my proposal for max response length (roughly 64 MB) which I think should not even be reached in realistic scenarios. It's basically just to have any upper bound limit.