Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
c5325c9
feat(wasm-dpp2,js-sdk): add shielded pool WASM bindings and JS SDK me…
QuantumExplorer Mar 12, 2026
65c02a6
revert: remove js-dash-sdk and wasm-dpp changes from shielded PR
QuantumExplorer Mar 12, 2026
8b71f68
feat(wasm-sdk): add shielded pool query methods
QuantumExplorer Mar 12, 2026
3256346
feat(wasm-dpp2): implement shielded proof result wrappers
QuantumExplorer Mar 12, 2026
d08e087
fix(wasm-sdk): correct computePlatformSighash doc for extraData format
QuantumExplorer Mar 12, 2026
98a20b6
fix(wasm-sdk): remove needless borrow in computePlatformSighash
QuantumExplorer Mar 12, 2026
5d4ee20
fix(wasm): address PR review feedback for shielded bindings
QuantumExplorer Mar 13, 2026
35b7ba4
fix(wasm-sdk): reject nullifiers that are not exactly 32 bytes
QuantumExplorer Mar 13, 2026
a87c6ad
Merge branch 'v3.1-dev' into feat/zk-wasm-js-bindings
QuantumExplorer Mar 13, 2026
0b48cd3
Merge branch 'v3.1-dev' into feat/zk-wasm-js-bindings
QuantumExplorer Mar 16, 2026
74e9290
feat(wasm): address all review feedback on shielded WASM bindings
QuantumExplorer Mar 16, 2026
7f216ec
feat(wasm): improve TypeScript interface definitions per shumkov review
QuantumExplorer Mar 16, 2026
c6a73b7
fix(wasm): use base64 string for all byte fields in OrchardActionJSON
QuantumExplorer Mar 16, 2026
4e29ccd
fix(wasm): use number | string for BigInt fields in JSON interfaces
QuantumExplorer Mar 16, 2026
fd850e1
fix(wasm): use typed parameters in shielded transition constructors
QuantumExplorer Mar 16, 2026
2db3796
fix(wasm): correct TS types to match actual serde output
QuantumExplorer Mar 16, 2026
c55a943
Merge branch 'v3.1-dev' into feat/zk-wasm-js-bindings
QuantumExplorer Mar 17, 2026
c6a2b3f
fix(wasm): change $version to $formatVersion, add Options constructor
QuantumExplorer Mar 17, 2026
df3de08
refactor(wasm-dpp2): unify rs-dpp/wasm-dpp2 typing and conversion con…
shumkov Apr 28, 2026
05e96f4
fix(wasm-dpp2): replace bare object types with typed JSON externs
shumkov Apr 28, 2026
26d7d0c
refactor(wasm-dpp2): convert shielded getters to property style
shumkov Apr 28, 2026
a84bfd6
Merge remote-tracking branch 'origin/v3.1-dev' into feat/zk-wasm-js-b…
shumkov Apr 28, 2026
42d02a1
style(wasm-dpp2): apply rustfmt
shumkov Apr 28, 2026
f200b65
fix(ci): resolve eslint and clippy lints surfaced by full CI run
shumkov Apr 28, 2026
321b18c
fix(platform-wallet): update spv_sync test to flat CoreChangeSet shape
shumkov Apr 28, 2026
c48b224
test(platform-value): lock in BinaryData byte-input deserialization
shumkov Apr 28, 2026
7ecdf5e
fix(wasm-dpp2): type outputAddress as Uint8Array in Object form + str…
shumkov Apr 29, 2026
f6ce62d
fix(dpp,wasm-dpp2): emit pooling as a string in JSON, fix broken TS t…
shumkov Apr 29, 2026
e87d363
fix(wasm-dpp2): enforce 216-byte encryptedNote at the boundary (mirro…
shumkov Apr 29, 2026
e3b301c
revert(wasm-dpp2): remove encryptedNote 216-byte check, codify thin-w…
shumkov Apr 29, 2026
19d7f54
refactor(wasm-dpp2): remove wasm-side size caps, defer to DPP validation
shumkov Apr 29, 2026
1dc3fbf
fix(wasm-dpp2): preserve Map entries in toJSON for verified-result wr…
shumkov Apr 29, 2026
cad8e46
fix(dpp): align address_funds::serde_helpers feature gate with module…
shumkov Apr 29, 2026
b9abce9
feat(js-evo-sdk): add ShieldedFacade for shielded pool queries
shumkov Apr 29, 2026
e82fc64
test(wasm-sdk): add unit tests for shielded query result types
shumkov Apr 29, 2026
1f7eef5
style(dpp): fmt pooling_serde Visitor and discriminant error
shumkov Apr 29, 2026
4a4b8f8
fix(dpp): satisfy clippy on serde_helpers gating and pooling_serde im…
shumkov Apr 29, 2026
e6b3655
test(wasm-sdk): add functional tests for shielded query methods
shumkov Apr 29, 2026
b78a6e9
refactor(wasm-sdk): drop redundant IShielded* TS interfaces in querie…
shumkov Apr 29, 2026
51da814
refactor(wasm-sdk): apply json_safe_fields convention to shielded wra…
shumkov Apr 29, 2026
ec2bcf2
chore(dpp): make serde_bytes and serde_bytes_var modules public
shumkov Apr 29, 2026
04cfcc4
refactor(wasm-sdk,js-evo-sdk): null/undefined convention for shielded…
shumkov Apr 29, 2026
624d3f7
fix(dpp): make serde_bytes / serde_bytes_var deserialize accept byte …
shumkov Apr 29, 2026
9d8d45b
refactor(wasm-sdk): drop bytes_b64 in shielded.rs, codify round-trip …
shumkov Apr 29, 2026
9acf091
refactor(wasm-sdk): use wasm_dpp2::utils::try_to_fixed_bytes in parse…
shumkov Apr 29, 2026
130fbda
refactor(wasm-sdk,js-evo-sdk): tighten Array typing on shielded queries
shumkov Apr 29, 2026
f8fa60e
style(wasm-sdk): fmt single-line wasm_bindgen attribute on getShielde…
shumkov Apr 29, 2026
607726e
refactor(wasm-dpp2): split proof_result into per-domain submodules
shumkov Apr 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/wasm-dpp2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub mod mock_bls;
pub mod platform_address;
pub mod public_key;
pub mod serialization;
pub mod shielded;
pub mod state_transitions;
pub mod tokens;
pub mod utils;
Expand Down Expand Up @@ -66,6 +67,10 @@ pub use platform_address::{
PlatformAddressWasm, default_fee_strategy, fee_strategy_from_steps,
fee_strategy_from_steps_or_default, outputs_to_btree_map, outputs_to_optional_btree_map,
};
pub use shielded::{
ShieldFromAssetLockTransitionWasm, ShieldTransitionWasm, ShieldedTransferTransitionWasm,
ShieldedWithdrawalTransitionWasm, UnshieldTransitionWasm,
};
pub use state_transitions::base::{GroupStateTransitionInfoWasm, StateTransitionWasm};
pub use state_transitions::proof_result::{StateTransitionProofResultTypeJs, convert_proof_result};
pub use tokens::*;
Expand Down
37 changes: 37 additions & 0 deletions packages/wasm-dpp2/src/shielded/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
pub mod shield_from_asset_lock_transition;
pub mod shield_transition;
pub mod shielded_transfer_transition;
pub mod shielded_withdrawal_transition;
pub mod unshield_transition;

pub use shield_from_asset_lock_transition::ShieldFromAssetLockTransitionWasm;
pub use shield_transition::ShieldTransitionWasm;
pub use shielded_transfer_transition::ShieldedTransferTransitionWasm;
pub use shielded_withdrawal_transition::ShieldedWithdrawalTransitionWasm;
pub use unshield_transition::UnshieldTransitionWasm;

use crate::error::WasmDppResult;
use wasm_bindgen::prelude::wasm_bindgen;

/// Compute the platform sighash from an Orchard bundle commitment and extra data.
///
/// `sighash = SHA-256("DashPlatformSighash" || bundleCommitment || extraData)`
///
/// - For shield and shielded_transfer transitions, `extraData` should be empty.
/// - For unshield transitions, `extraData` = serialized `outputAddress` bytes.
/// - For shielded withdrawal transitions, `extraData` = `outputScript` bytes.
#[wasm_bindgen(js_name = computePlatformSighash)]
pub fn compute_platform_sighash_wasm(
bundle_commitment: &[u8],
extra_data: &[u8],
) -> WasmDppResult<Vec<u8>> {
if bundle_commitment.len() != 32 {
return Err(crate::error::WasmDppError::invalid_argument(format!(
"bundleCommitment must be exactly 32 bytes, got {}",
bundle_commitment.len()
)));
}
let commitment: &[u8; 32] = bundle_commitment.try_into().expect("checked length above");
let result = dpp::shielded::compute_platform_sighash(commitment, extra_data);
Ok(result.to_vec())
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
271 changes: 271 additions & 0 deletions packages/wasm-dpp2/src/shielded/shield_from_asset_lock_transition.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
use crate::asset_lock_proof::AssetLockProofWasm;
use crate::error::{WasmDppError, WasmDppResult};
use crate::identifier::IdentifierWasm;
use crate::utils::try_from_options;
use crate::{impl_wasm_conversions_serde, impl_wasm_type_info};
use dpp::platform_value::BinaryData;
use dpp::serialization::{PlatformDeserializable, PlatformSerializable};
use dpp::state_transition::shield_from_asset_lock_transition::ShieldFromAssetLockTransition;
use dpp::state_transition::shield_from_asset_lock_transition::v0::ShieldFromAssetLockTransitionV0;
use dpp::state_transition::{StateTransition, StateTransitionLike};
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct ShieldFromAssetLockTransitionSimpleFields {
#[serde(default)]
actions: Vec<dpp::shielded::SerializedAction>,
Comment thread
shumkov marked this conversation as resolved.
Outdated
#[serde(default)]
value_balance: u64,
#[serde(default)]
anchor: Vec<u8>,
#[serde(default)]
proof: Vec<u8>,
#[serde(default)]
binding_signature: Vec<u8>,
#[serde(default)]
signature: Vec<u8>,
}
Comment thread
shumkov marked this conversation as resolved.

#[wasm_bindgen(typescript_custom_section)]
const TS_TYPES: &str = r#"
/**
* A serialized Orchard action (spend-output pair) in binary/Object form.
*/
export interface SerializedOrchardAction {
nullifier: Uint8Array;
rk: Uint8Array;
cmx: Uint8Array;
encryptedNote: Uint8Array;
cvNet: Uint8Array;
spendAuthSig: Uint8Array;
}

/**
* A serialized Orchard action (spend-output pair) in JSON form.
*/
export interface SerializedOrchardActionJSON {
nullifier: string;
rk: string;
cmx: string;
encryptedNote: string;
cvNet: string;
spendAuthSig: string;
}

/**
* Options for constructing a ShieldFromAssetLockTransition.
* Uses WASM instance types for complex fields like AssetLockProof.
*/
export interface ShieldFromAssetLockTransitionOptions {
assetLockProof: AssetLockProof;
actions: SerializedOrchardAction[];
valueBalance: bigint;
anchor: Uint8Array;
proof: Uint8Array;
bindingSignature: Uint8Array;
signature: Uint8Array;
}

/**
* ShieldFromAssetLockTransition serialized as a plain object.
*/
export interface ShieldFromAssetLockTransitionObject {
$formatVersion: string;
assetLockProof: AssetLockProofObject;
actions: SerializedOrchardAction[];
valueBalance: bigint;
anchor: Uint8Array;
proof: Uint8Array;
bindingSignature: Uint8Array;
signature: Uint8Array;
}

/**
* ShieldFromAssetLockTransition serialized as JSON (human-readable).
*/
export interface ShieldFromAssetLockTransitionJSON {
$formatVersion: string;
assetLockProof: AssetLockProofJSON;
actions: SerializedOrchardActionJSON[];
valueBalance: number | string;
anchor: string;
proof: string;
bindingSignature: string;
signature: string;
}
"#;

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(typescript_type = "ShieldFromAssetLockTransitionOptions")]
pub type ShieldFromAssetLockTransitionOptionsJs;

#[wasm_bindgen(typescript_type = "ShieldFromAssetLockTransitionObject")]
pub type ShieldFromAssetLockTransitionObjectJs;

#[wasm_bindgen(typescript_type = "ShieldFromAssetLockTransitionJSON")]
pub type ShieldFromAssetLockTransitionJSONJs;
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(transparent)]
#[wasm_bindgen(js_name = ShieldFromAssetLockTransition)]
pub struct ShieldFromAssetLockTransitionWasm(ShieldFromAssetLockTransition);

impl From<ShieldFromAssetLockTransition> for ShieldFromAssetLockTransitionWasm {
fn from(v: ShieldFromAssetLockTransition) -> Self {
ShieldFromAssetLockTransitionWasm(v)
}
}

impl From<ShieldFromAssetLockTransitionWasm> for ShieldFromAssetLockTransition {
fn from(v: ShieldFromAssetLockTransitionWasm) -> Self {
v.0
}
}

#[wasm_bindgen(js_class = ShieldFromAssetLockTransition)]
impl ShieldFromAssetLockTransitionWasm {
Comment thread
QuantumExplorer marked this conversation as resolved.
#[wasm_bindgen(constructor)]
pub fn new(
options: ShieldFromAssetLockTransitionOptionsJs,
) -> WasmDppResult<ShieldFromAssetLockTransitionWasm> {
// Extract assetLockProof as a WASM instance (required)
let asset_lock: AssetLockProofWasm = try_from_options(&options, "assetLockProof")?;

// Extract remaining simple fields via serde
let fields: ShieldFromAssetLockTransitionSimpleFields =
serde_wasm_bindgen::from_value(options.into())
.map_err(|e| WasmDppError::serialization(e.to_string()))?;

let anchor: [u8; 32] = fields
.anchor
.try_into()
.map_err(|_| WasmDppError::invalid_argument("anchor must be exactly 32 bytes"))?;

let binding_signature: [u8; 64] = fields.binding_signature.try_into().map_err(|_| {
WasmDppError::invalid_argument("bindingSignature must be exactly 64 bytes")
})?;

Ok(ShieldFromAssetLockTransitionWasm(
ShieldFromAssetLockTransition::V0(ShieldFromAssetLockTransitionV0 {
asset_lock_proof: asset_lock.into(),
actions: fields.actions,
value_balance: fields.value_balance,
anchor,
proof: fields.proof,
binding_signature,
signature: BinaryData::from(fields.signature),
}),
))
}

#[wasm_bindgen(js_name = getType)]
pub fn get_type(&self) -> u8 {
self.0.state_transition_type() as u8
}

/// Returns the asset lock proof as a JS value.
#[wasm_bindgen(js_name = getAssetLockProof)]
pub fn get_asset_lock_proof(&self) -> WasmDppResult<JsValue> {
Comment thread
shumkov marked this conversation as resolved.
Outdated
let proof = match &self.0 {
ShieldFromAssetLockTransition::V0(v0) => &v0.asset_lock_proof,
};
serde_wasm_bindgen::to_value(proof).map_err(|e| WasmDppError::serialization(e.to_string()))
Comment thread
shumkov marked this conversation as resolved.
Outdated
}

/// Returns the serialized Orchard actions as a JS array.
#[wasm_bindgen(js_name = getActions)]
pub fn get_actions(&self) -> WasmDppResult<JsValue> {
Comment thread
shumkov marked this conversation as resolved.
Outdated
let inner = match &self.0 {
ShieldFromAssetLockTransition::V0(v0) => &v0.actions,
};
serde_wasm_bindgen::to_value(inner).map_err(|e| WasmDppError::serialization(e.to_string()))
}

/// Returns the net value balance.
#[wasm_bindgen(js_name = getValueBalance)]
pub fn get_value_balance(&self) -> u64 {
match &self.0 {
ShieldFromAssetLockTransition::V0(v0) => v0.value_balance,
}
}

/// Returns the anchor (32-byte Merkle root).
#[wasm_bindgen(js_name = getAnchor)]
pub fn get_anchor(&self) -> Vec<u8> {
match &self.0 {
ShieldFromAssetLockTransition::V0(v0) => v0.anchor.to_vec(),
}
}

/// Returns the Halo2 proof bytes.
#[wasm_bindgen(js_name = getProof)]
pub fn get_proof(&self) -> Vec<u8> {
match &self.0 {
ShieldFromAssetLockTransition::V0(v0) => v0.proof.clone(),
}
}

/// Returns the RedPallas binding signature (64 bytes).
#[wasm_bindgen(js_name = getBindingSignature)]
pub fn get_binding_signature(&self) -> Vec<u8> {
match &self.0 {
ShieldFromAssetLockTransition::V0(v0) => v0.binding_signature.to_vec(),
}
}

/// Returns the ECDSA signature.
#[wasm_bindgen(js_name = getSignature)]
pub fn get_signature(&self) -> Vec<u8> {
match &self.0 {
ShieldFromAssetLockTransition::V0(v0) => v0.signature.to_vec(),
}
}

#[wasm_bindgen(js_name = getModifiedDataIds)]
pub fn modified_data_ids(&self) -> Vec<IdentifierWasm> {
self.0
.modified_data_ids()
.into_iter()
.map(IdentifierWasm::from)
.collect()
}

#[wasm_bindgen(js_name = toBytes)]
pub fn to_bytes(&self) -> WasmDppResult<Vec<u8>> {
Ok(PlatformSerializable::serialize_to_bytes(
&StateTransition::ShieldFromAssetLock(self.0.clone()),
)?)
}

#[wasm_bindgen(js_name = fromBytes)]
pub fn from_bytes(bytes: Vec<u8>) -> WasmDppResult<ShieldFromAssetLockTransitionWasm> {
let st = StateTransition::deserialize_from_bytes(&bytes)?;
match st {
StateTransition::ShieldFromAssetLock(inner) => Ok(inner.into()),
_ => Err(WasmDppError::invalid_argument(
"Invalid state transition type: expected ShieldFromAssetLock",
)),
}
}

#[wasm_bindgen(js_name = toStateTransition)]
pub fn to_state_transition(&self) -> crate::state_transitions::base::StateTransitionWasm {
StateTransition::ShieldFromAssetLock(self.0.clone()).into()
}
}

impl_wasm_conversions_serde!(
ShieldFromAssetLockTransitionWasm,
ShieldFromAssetLockTransition,
ShieldFromAssetLockTransitionObjectJs,
ShieldFromAssetLockTransitionJSONJs
);

impl_wasm_type_info!(
ShieldFromAssetLockTransitionWasm,
ShieldFromAssetLockTransition
);
Loading
Loading