use crate::imports::*;
use kaspa_consensus_core::header as native;
use kaspa_hashes::Hash;
#[derive(Clone, Debug, Serialize, Deserialize, CastFromJs)]
#[serde(rename_all = "camelCase")]
#[wasm_bindgen(inspectable)]
pub struct CompressedParents {
inner: native::CompressedParents,
}
#[wasm_bindgen]
impl CompressedParents {
#[wasm_bindgen(constructor)]
pub fn new(js_value: JsValue) -> Result<CompressedParents, JsError> {
let inner = serde_wasm_bindgen::from_value(js_value)?;
Ok(CompressedParents { inner })
}
#[wasm_bindgen]
pub fn get(&self, index: usize) -> Result<JsValue, JsError> {
Ok(serde_wasm_bindgen::to_value(&self.inner.get(index))?)
}
#[wasm_bindgen(js_name = expandedLen)]
pub fn expanded_len(&self) -> usize {
self.inner.expanded_len()
}
#[wasm_bindgen(js_name = toExpanded)]
pub fn to_expanded(&self) -> Result<JsValue, JsError> {
let expanded: Vec<Vec<Hash>> = self.inner.clone().into();
Ok(serde_wasm_bindgen::to_value(&expanded)?)
}
}
impl From<native::CompressedParents> for CompressedParents {
fn from(inner: native::CompressedParents) -> Self {
Self { inner }
}
}
impl From<CompressedParents> for native::CompressedParents {
fn from(wrapped: CompressedParents) -> Self {
wrapped.inner
}
}
impl TryCastFromJs for CompressedParents {
type Error = Error;
fn try_cast_from<'a, R>(value: &'a R) -> Result<Cast<'a, Self>, Self::Error>
where
R: AsRef<JsValue> + 'a,
{
Self::resolve_cast(value, || {
let array = js_sys::Array::from(value.as_ref());
let runs = array
.iter()
.map(|run_js| {
let run_array = js_sys::Array::from(&run_js);
if run_array.length() != 2 {
return Err(Error::Custom("Invalid parent run, must be a tuple of [level, parents[]]".to_string()));
}
let level = run_array.get(0).try_as_u8().map_err(|_| Error::Custom("Invalid level in parent runs".to_string()))?;
let parents_js = run_array.get(1);
let parents_array = js_sys::Array::from(&parents_js);
let parents =
parents_array.iter().map(|hash_js| hash_js.try_into_owned()).collect::<std::result::Result<Vec<Hash>, _>>()?;
Ok((level, parents))
})
.collect::<std::result::Result<Vec<(u8, Vec<Hash>)>, Error>>()?;
let inner: native::CompressedParents = runs.try_into()?;
Ok(Self { inner }.into())
})
}
}
#[cfg(all(test, target_arch = "wasm32"))]
mod tests {
use super::*;
use kaspa_consensus_core::errors::header::CompressedParentsError;
use wasm_bindgen_test::wasm_bindgen_test;
#[wasm_bindgen_test]
fn oversized_compressed_parents_js_cast_returns_error() {
let parents = js_sys::Array::new();
let parent = JsValue::from_str(&"00".repeat(32));
for _ in 0..2049 {
parents.push(&parent);
}
let run = js_sys::Array::new();
run.push(&JsValue::from(u8::MAX));
run.push(&parents);
let runs = js_sys::Array::new();
runs.push(&run);
assert!(matches!(
CompressedParents::try_cast_from(&runs),
Err(Error::CompressedParents(CompressedParentsError::SizeExceeded))
));
}
}