Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- [FIX][cli] `notes --send` now rejects a recipient address whose network doesn't match the client's configured network, instead of silently sending to it. ([#2525](https://github.com/0xMiden/rust-sdk/pull/2525)).

### Breaking Changes

* [BREAKING][type][rust] Added the `TransactionRequestError::SwapNoteWithZeroAsset` variant, so exhaustive matches on `TransactionRequestError` must handle it ([#2459](https://github.com/0xMiden/rust-sdk/pull/2459)).
Expand Down
28 changes: 27 additions & 1 deletion bin/miden-cli/src/commands/notes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,14 @@ async fn send<AUTH: Keystore + Sync>(
let note: Note = note_record
.try_into()
.map_err(|e| CliError::from(ClientError::NoteRecordConversionError(e)))?;
let (_netid, address) = Address::decode(address).map_err(|e| CliError::Input(e.to_string()))?;
let (address_network_id, address) =
Address::decode(address).map_err(|e| CliError::Input(e.to_string()))?;
let client_network_id = client.network_id().await?;
if address_network_id != client_network_id {
return Err(CliError::Input(format!(
"Address network `{address_network_id}` does not match configured network `{client_network_id}`",
)));
}

match block_hint {
Some(block_hint) => {
Expand Down Expand Up @@ -525,6 +532,7 @@ fn note_summary(
mod tests {
use miden_client::Word;
use miden_client::account::AccountId;
use miden_client::address::{Address, NetworkId};
use miden_client::note::{
Note,
NoteAssets,
Expand Down Expand Up @@ -564,4 +572,22 @@ mod tests {
assert_eq!(summary.sender, sender.to_string());
assert_eq!(summary.tag, tag.to_string());
}

/// `notes --send` must reject an address whose network doesn't match the client's
/// configured network. This checks the underlying decode step that the fix relies on:
/// encoding an address for one network and decoding it must not report a match against a
/// different network.
#[test]
fn decoded_address_network_differs_across_networks() {
let account_id =
AccountId::try_from(ACCOUNT_ID_REGULAR_PRIVATE_ACCOUNT_UPDATABLE_CODE).unwrap();
let address = Address::new(account_id);

let encoded_for_testnet = address.encode(NetworkId::Testnet);
let (decoded_network_id, _decoded_address) =
Address::decode(&encoded_for_testnet).unwrap();

assert_eq!(decoded_network_id, NetworkId::Testnet);
assert_ne!(decoded_network_id, NetworkId::Mainnet);
}
}