-
Notifications
You must be signed in to change notification settings - Fork 50
feat(wasm-sdk): add shielded pool WASM bindings and query methods #3235
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
base: v3.1-dev
Are you sure you want to change the base?
Changes from all commits
c5325c9
65c02a6
8b71f68
3256346
d08e087
98a20b6
5d4ee20
35b7ba4
a87c6ad
0b48cd3
74e9290
7f216ec
c6a73b7
4e29ccd
fd850e1
2db3796
c55a943
c6a2b3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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()) | ||
| } | ||
| 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>, | ||
| #[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 on lines
+14
to
+29
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. 🟡 Suggestion: Every field in This creates a confusing failure mode: instead of getting a clear "missing field" error at the WASM boundary, a JS caller omitting required fields gets a structurally valid but logically broken transition that will fail much later during validation. Consider removing source: ['claude', 'codex'] 🤖 Fix this with AI agents |
||
|
|
||
| #[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 { | ||
QuantumExplorer marked this conversation as resolved.
Show resolved
Hide 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> { | ||
| 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())) | ||
| } | ||
|
|
||
| /// Returns the serialized Orchard actions as a JS array. | ||
| #[wasm_bindgen(js_name = getActions)] | ||
| pub fn get_actions(&self) -> WasmDppResult<JsValue> { | ||
| 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 | ||
| ); | ||
Uh oh!
There was an error while loading. Please reload this page.