Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions packages/rs-dpp/src/shielded/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ mod unshield;

pub use self::shield::build_shield_transition;
pub use shield_from_asset_lock::build_shield_from_asset_lock_transition;
#[cfg(feature = "core_key_wallet")]
pub use shield_from_asset_lock::build_shield_from_asset_lock_transition_with_signer;
pub use shielded_transfer::build_shielded_transfer_transition;
pub use shielded_withdrawal::build_shielded_withdrawal_transition;
pub use unshield::build_unshield_transition;
Expand Down
60 changes: 60 additions & 0 deletions packages/rs-dpp/src/shielded/builder/shield_from_asset_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,66 @@ pub fn build_shield_from_asset_lock_transition<P: OrchardProver>(
)
}

/// Builds a ShieldFromAssetLock state transition where the
/// asset-lock-proof signature is produced by an external
/// [`key_wallet::signer::Signer`] (Swift / hardware-wallet / HSM
/// flow). The raw private key never crosses the FFI boundary;
/// derive + sign + zeroise happen inside the signer.
///
/// # Parameters
/// - `recipient` - Orchard address to receive the shielded note
/// - `shield_amount` - Amount of credits to shield (from the asset lock)
/// - `asset_lock_proof` - Proof that funds are locked on core chain
/// - `asset_lock_proof_path` - BIP32 path to the asset-lock key inside `asset_lock_signer`
/// - `asset_lock_signer` - External signer that produces the outer ECDSA signature
/// - `prover` - Orchard prover (holds the Halo 2 proving key)
/// - `memo` - 36-byte structured memo for the recipient (4-byte type tag + 32-byte payload)
/// - `platform_version` - Protocol version
#[cfg(feature = "core_key_wallet")]
#[allow(clippy::too_many_arguments)]
pub async fn build_shield_from_asset_lock_transition_with_signer<P, AS>(
recipient: &OrchardAddress,
shield_amount: u64,
asset_lock_proof: AssetLockProof,
asset_lock_proof_path: &::key_wallet::bip32::DerivationPath,
asset_lock_signer: &AS,
prover: &P,
memo: [u8; 36],
platform_version: &PlatformVersion,
) -> Result<StateTransition, ProtocolError>
where
P: OrchardProver,
AS: ::key_wallet::signer::Signer,
{
let bundle = build_output_only_bundle(recipient, shield_amount, memo, prover)?;
let sb = serialize_authorized_bundle(&bundle);

// For output-only bundles, Orchard value_balance is negative (value flowing in).
// Convert to u64 (absolute amount entering the pool).
let value_balance = sb
.value_balance
.checked_neg()
.and_then(|v| u64::try_from(v).ok())
.ok_or_else(|| {
ProtocolError::ShieldedBuildError(
"shield_from_asset_lock: bundle value_balance is not negative".to_string(),
)
})?;

ShieldFromAssetLockTransition::try_from_asset_lock_with_bundle_and_signer(
asset_lock_proof,
asset_lock_proof_path,
asset_lock_signer,
sb.actions,
value_balance,
sb.anchor,
sb.proof,
sb.binding_signature,
platform_version,
)
.await
}

#[cfg(test)]
mod tests {
use super::super::{build_output_only_bundle, serialize_authorized_bundle};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,48 @@ impl ShieldFromAssetLockTransitionMethodsV0 for ShieldFromAssetLockTransition {
}),
}
}

#[cfg(all(feature = "state-transition-signing", feature = "core_key_wallet"))]
async fn try_from_asset_lock_with_bundle_and_signer<AS>(
asset_lock_proof: AssetLockProof,
asset_lock_proof_path: &::key_wallet::bip32::DerivationPath,
asset_lock_signer: &AS,
actions: Vec<SerializedAction>,
value_balance: u64,
anchor: [u8; 32],
proof: Vec<u8>,
binding_signature: [u8; 64],
platform_version: &PlatformVersion,
) -> Result<StateTransition, ProtocolError>
where
AS: ::key_wallet::signer::Signer,
{
match platform_version
.dpp
.state_transition_serialization_versions
.shield_from_asset_lock_state_transition
.default_current_version
{
0 => {
ShieldFromAssetLockTransitionV0::try_from_asset_lock_with_bundle_and_signer(
asset_lock_proof,
asset_lock_proof_path,
asset_lock_signer,
actions,
value_balance,
anchor,
proof,
binding_signature,
platform_version,
)
.await
}
version => Err(ProtocolError::UnknownVersionMismatch {
method: "ShieldFromAssetLockTransition::try_from_asset_lock_with_bundle_and_signer"
.to_string(),
known_versions: vec![0],
received: version,
}),
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,33 @@ pub trait ShieldFromAssetLockTransitionMethodsV0 {
platform_version: &PlatformVersion,
) -> Result<StateTransition, ProtocolError>;

/// Build a `ShieldFromAssetLock` state transition where the
/// asset-lock-proof signature is produced by an external
/// [`key_wallet::signer::Signer`].
///
/// `asset_lock_signer` produces the outer state-transition ECDSA
/// signature for the key at `asset_lock_proof_path` — atomically
/// deriving, signing, and zeroising inside the signer's trust
/// boundary. This is the signing path used by hosts that hold
/// their private keys outside Rust (the iOS Swift SDK, hardware
/// wallets, remote signers); the raw key never crosses the FFI
/// boundary.
#[cfg(all(feature = "state-transition-signing", feature = "core_key_wallet"))]
#[allow(clippy::too_many_arguments)]
async fn try_from_asset_lock_with_bundle_and_signer<AS>(
asset_lock_proof: AssetLockProof,
asset_lock_proof_path: &::key_wallet::bip32::DerivationPath,
asset_lock_signer: &AS,
actions: Vec<SerializedAction>,
value_balance: u64,
anchor: [u8; 32],
proof: Vec<u8>,
binding_signature: [u8; 64],
platform_version: &PlatformVersion,
) -> Result<StateTransition, ProtocolError>
where
AS: ::key_wallet::signer::Signer;

/// Get State Transition Type
fn get_type() -> StateTransitionType {
StateTransitionType::ShieldFromAssetLock
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
pub mod fields;
pub mod methods;
mod proved;
#[cfg(all(
test,
feature = "state-transition-signing",
feature = "core_key_wallet"
))]
mod signing_tests;
mod state_transition_estimated_fee_validation;
mod state_transition_like;
mod state_transition_validation;
Expand Down
Loading
Loading