Skip to content
Merged
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
73 changes: 70 additions & 3 deletions packages/rs-platform-wallet/src/changeset/serde_adapters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,77 @@ pub mod optional_asset_lock_proof {
let bytes = Option::<Vec<u8>>::deserialize(deserializer)?;
bytes
.map(|b| {
dpp::bincode::decode_from_slice(&b, dpp::bincode::config::standard())
.map(|(proof, _)| proof)
.map_err(serde::de::Error::custom)
let (proof, consumed) =
dpp::bincode::decode_from_slice(&b, dpp::bincode::config::standard())
.map_err(serde::de::Error::custom)?;
// `decode_from_slice` stops at the value's end without
// rejecting trailing bytes — but this blob holds exactly
// one proof, so a longer payload is corruption (or
// smuggled data), not a valid encoding. Fail loudly
// rather than silently dropping the tail.
if consumed != b.len() {
return Err(serde::de::Error::custom(format!(
"asset lock proof blob has {} trailing byte(s)",
b.len() - consumed
)));
}
Ok(proof)
})
.transpose()
}
}

#[cfg(test)]
mod optional_asset_lock_proof_tests {
use dpp::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof;
use dpp::prelude::AssetLockProof;

#[derive(serde::Serialize, serde::Deserialize)]
struct Carrier {
#[serde(with = "super::optional_asset_lock_proof")]
proof: Option<AssetLockProof>,
}

fn chain_proof() -> Option<AssetLockProof> {
Some(AssetLockProof::Chain(ChainAssetLockProof {
core_chain_locked_height: 411_495,
out_point: dashcore::OutPoint::null(),
}))
}

/// Exact-length payloads round-trip; a payload with appended bytes
/// is corruption and must fail decode instead of silently dropping
/// the tail.
#[test]
fn rejects_trailing_bytes() {
let encoded = dpp::bincode::serde::encode_to_vec(
&Carrier {
proof: chain_proof(),
},
dpp::bincode::config::standard(),
)
.expect("encode");
let (decoded, _): (Carrier, _) =
dpp::bincode::serde::decode_from_slice(&encoded, dpp::bincode::config::standard())
.expect("exact payload decodes");
assert_eq!(decoded.proof, chain_proof());

// Corrupt the blob by appending a byte INSIDE the proof bytes:
// re-encode with a tampered inner vec. The adapter serializes
// the proof as Option<Vec<u8>>, so build that shape directly.
let proof_bytes =
dpp::bincode::encode_to_vec(chain_proof().unwrap(), dpp::bincode::config::standard())
.expect("proof bytes");
let mut padded = proof_bytes;
padded.push(0xAA);
let tampered =
dpp::bincode::serde::encode_to_vec(&(Some(padded),), dpp::bincode::config::standard())
.expect("encode tampered carrier");
let result: Result<(Carrier, _), _> =
dpp::bincode::serde::decode_from_slice(&tampered, dpp::bincode::config::standard());
assert!(
result.is_err(),
"a proof blob with trailing bytes must be rejected"
);
}
}
Loading