Skip to content

Commit

Permalink
hex view for ScriptBuilder (kaspanet#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
aspect authored Jul 16, 2024
1 parent 4c0b148 commit 4a8f2cd
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 6 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,13 @@ faster-hex = "0.6.1" # TODO "0.8.1" - fails unit tests
fixedstr = { version = "0.5.4", features = ["serde"] }
flate2 = "1.0.28"
futures = { version = "0.3.29" }
futures-util = { version = "0.3.29", default-features = false, features = [
"alloc",
] }
futures-util = { version = "0.3.29", default-features = false, features = ["alloc"] }
getrandom = { version = "0.2.10", features = ["js"] }
h2 = "0.3.21"
heapless = "0.7.16"
hex = { version = "0.4.3", features = ["serde"] }
hex-literal = "0.4.1"
hexplay = "0.3.0"
hmac = { version = "0.12.1", default-features = false }
home = "0.5.5"
igd-next = { version = "0.14.2", features = ["aio_tokio"] }
Expand Down
1 change: 1 addition & 0 deletions crypto/txscript/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ wasm32-sdk = []
blake2b_simd.workspace = true
borsh.workspace = true
cfg-if.workspace = true
hexplay.workspace = true
indexmap.workspace = true
itertools.workspace = true
kaspa-addresses.workspace = true
Expand Down
11 changes: 11 additions & 0 deletions crypto/txscript/src/script_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
opcodes::{codes::*, OP_1_NEGATE_VAL, OP_DATA_MAX_VAL, OP_DATA_MIN_VAL, OP_SMALL_INT_MAX_VAL},
MAX_SCRIPTS_SIZE, MAX_SCRIPT_ELEMENT_SIZE,
};
use hexplay::{HexView, HexViewBuilder};
use thiserror::Error;

/// DEFAULT_SCRIPT_ALLOC is the default size used for the backing array
Expand Down Expand Up @@ -248,6 +249,16 @@ impl ScriptBuilder {
let trimmed = &buffer[0..trimmed_size];
self.add_data(trimmed)
}

/// Return [`HexViewBuilder`] for the script
pub fn hex_view_builder(&self) -> HexViewBuilder<'_> {
HexViewBuilder::new(&self.script)
}

/// Return ready to use [`HexView`] for the script
pub fn hex_view(&self, offset: usize, width: usize) -> HexView<'_> {
HexViewBuilder::new(&self.script).address_offset(offset).row_width(width).finish()
}
}

impl Default for ScriptBuilder {
Expand Down
10 changes: 10 additions & 0 deletions crypto/txscript/src/wasm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::result::Result;
use crate::{script_builder as native, standard};
use kaspa_consensus_core::tx::ScriptPublicKey;
use kaspa_utils::hex::ToHex;
use kaspa_wasm_core::hex::{HexViewConfig, HexViewConfigT};
use kaspa_wasm_core::types::{BinaryT, HexString};
use std::cell::{Ref, RefCell, RefMut};
use std::rc::Rc;
Expand Down Expand Up @@ -168,4 +169,13 @@ impl ScriptBuilder {

Ok(generated_script.to_hex().into())
}

#[wasm_bindgen(js_name = "hexView")]
pub fn hex_view(&self, args: Option<HexViewConfigT>) -> Result<String> {
let inner = self.inner();
let script = inner.script();

let config = args.map(HexViewConfig::try_from).transpose()?.unwrap_or_default();
Ok(config.build(script).to_string())
}
}
2 changes: 2 additions & 0 deletions wasm/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ wasm32-sdk = []
wasm-bindgen.workspace = true
js-sys.workspace = true
faster-hex.workspace = true
hexplay.workspace = true
workflow-wasm.workspace = true

[lints]
workspace = true
152 changes: 152 additions & 0 deletions wasm/core/src/hex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//!
//! Hex module provides a way to display binary data in a human-readable format.
//!
use hexplay::{
color::{Color, Spec},
HexView, HexViewBuilder,
};
use std::ops::Range;
use std::str::FromStr;
use wasm_bindgen::prelude::*;
use workflow_wasm::prelude::*;

type Result<T> = std::result::Result<T, JsValue>;

#[derive(Default)]
pub struct HexViewConfig {
pub offset: Option<usize>,
pub replace_char: Option<char>,
pub width: Option<usize>,
pub colors: Option<Vec<(Spec, Range<usize>)>>,
}

impl HexViewConfig {
pub fn build(self, slice: &[u8]) -> HexView<'_> {
let mut builder = HexViewBuilder::new(slice);

if let Some(offset) = self.offset {
builder = builder.address_offset(offset);
}

if let Some(replace_char) = self.replace_char {
builder = builder.replacement_character(replace_char);
}

if let Some(width) = self.width {
builder = builder.row_width(width);
}

if let Some(colors) = self.colors {
if !colors.is_empty() {
builder = builder.add_colors(colors);
}
}

builder.finish()
}
}

pub struct ColorRange {
pub color: Option<Color>,
pub background: Option<Color>,
pub range: Range<usize>,
}

impl ColorRange {
fn new(color: Option<Color>, background: Option<Color>, range: Range<usize>) -> Self {
Self { color, background, range }
}

fn into_tuple(self) -> (Spec, Range<usize>) {
let mut spec = Spec::new();
spec.set_fg(self.color);
spec.set_bg(self.background);

(spec, self.range)
}
}

#[wasm_bindgen(typescript_custom_section)]
const TS_HEX_VIEW: &'static str = r#"
/**
* Color range configuration for Hex View.
*
* @category General
*/
export interface IHexViewColor {
start: number;
end: number;
color?: string;
background?: string;
}
/**
* Configuration interface for Hex View.
*
* @category General
*/
export interface IHexViewConfig {
offset? : number;
replacementCharacter? : string;
width? : number;
colors? : IHexViewColor[];
}
"#;

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(typescript_type = "IHexViewColor")]
pub type HexViewColorT;
#[wasm_bindgen(extends = js_sys::Array, typescript_type = "IHexViewColor[]")]
pub type HexViewColorArrayT;
#[wasm_bindgen(typescript_type = "IHexViewConfig")]
pub type HexViewConfigT;
}

impl TryFrom<JsValue> for ColorRange {
type Error = JsValue;
fn try_from(js_value: JsValue) -> Result<ColorRange> {
if let Some(object) = js_sys::Object::try_from(&js_value) {
let start = object.get_u32("start")? as usize;
let end = object.get_u32("end")? as usize;

let color = object.get_string("color").ok();
let color =
color.map(|color| Color::from_str(color.as_str()).map_err(|e| JsValue::from_str(&e.to_string()))).transpose()?;

let background = object.get_string("background").ok();
let background = background
.map(|background| Color::from_str(background.as_str()).map_err(|e| JsValue::from_str(&e.to_string())))
.transpose()?;

Ok(ColorRange::new(color, background, start..end))
} else {
Err(JsValue::from_str("color range must be an object"))
}
}
}

pub fn try_to_color_vec(js_value: JsValue) -> Result<Vec<(Spec, Range<usize>)>> {
if js_value.is_array() {
let list = js_sys::Array::from(&js_value).iter().map(TryFrom::try_from).collect::<Result<Vec<ColorRange>>>()?;
Ok(list.into_iter().map(ColorRange::into_tuple).collect::<Vec<_>>())
} else {
let tuple = ColorRange::try_from(js_value).map(ColorRange::into_tuple)?;
Ok(vec![tuple])
}
}

impl TryFrom<HexViewConfigT> for HexViewConfig {
type Error = JsValue;
fn try_from(js_value: HexViewConfigT) -> Result<HexViewConfig> {
let object = js_sys::Object::try_from(&js_value).ok_or_else(|| JsValue::from_str("HexView config must be an object"))?;

let offset = object.get_u32("offset").ok().map(|v| v as usize);
let replace_char = object.get_string("replacementCharacter").ok().map(|s| s.chars().next().unwrap_or(' '));
let width = object.get_u32("width").ok().map(|v| v as usize);
let colors = object.get_value("colors").ok().map(try_to_color_vec).transpose()?;

Ok(HexViewConfig { offset, replace_char, width, colors })
}
}
3 changes: 1 addition & 2 deletions wasm/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pub mod events;
pub mod hex;
pub mod types;

// pub use types::*;
2 changes: 1 addition & 1 deletion wasm/core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl From<String> for HexString {
impl TryFrom<HexString> for String {
type Error = &'static str;

fn try_from(value: HexString) -> Result<String, Self::Error> {
fn try_from(value: HexString) -> std::result::Result<String, Self::Error> {
value.as_string().ok_or("Supplied value is not a string")
}
}
Expand Down

0 comments on commit 4a8f2cd

Please sign in to comment.