mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-24 22:19:37 -04:00
feat(monero-sys): Sign message (#450)
This commit is contained in:
parent
38332ab79f
commit
56722a5780
4 changed files with 153 additions and 0 deletions
|
@ -200,6 +200,15 @@ namespace Monero
|
||||||
return std::make_unique<std::string>(key);
|
return std::make_unique<std::string>(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign a message with the wallet's private key
|
||||||
|
*/
|
||||||
|
inline std::unique_ptr<std::string> signMessage(Wallet &wallet, const std::string &message, const std::string &address, bool sign_with_view_key)
|
||||||
|
{
|
||||||
|
auto signature = wallet.signMessage(message, address, sign_with_view_key);
|
||||||
|
return std::make_unique<std::string>(signature);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the seed of the wallet.
|
* Get the seed of the wallet.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -270,6 +270,14 @@ pub mod ffi {
|
||||||
self: Pin<&mut Wallet>,
|
self: Pin<&mut Wallet>,
|
||||||
tx: *mut PendingTransaction,
|
tx: *mut PendingTransaction,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
|
||||||
|
/// Sign a message with the wallet's private key.
|
||||||
|
fn signMessage(
|
||||||
|
wallet: Pin<&mut Wallet>,
|
||||||
|
message: &CxxString,
|
||||||
|
address: &CxxString,
|
||||||
|
sign_with_view_key: bool,
|
||||||
|
) -> Result<UniquePtr<CxxString>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -675,6 +675,30 @@ impl WalletHandle {
|
||||||
// Signal success
|
// Signal success
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sign a message with the wallet's private key.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `message` - The message to sign (arbitrary byte data)
|
||||||
|
/// * `address` - The address to use for signing (uses main address if None)
|
||||||
|
/// * `sign_with_view_key` - Whether to sign with view key instead of spend key (default: false)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A proof type prefix + base58 encoded signature
|
||||||
|
pub async fn sign_message(
|
||||||
|
&self,
|
||||||
|
message: &str,
|
||||||
|
address: Option<&str>,
|
||||||
|
sign_with_view_key: bool,
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
let message = message.to_string();
|
||||||
|
let address = address.map(|s| s.to_string());
|
||||||
|
|
||||||
|
self.call(move |wallet| {
|
||||||
|
wallet.sign_message(&message, address.as_deref(), sign_with_view_key)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
|
@ -1727,6 +1751,36 @@ impl FfiWallet {
|
||||||
.expect("Shouldn't panic")
|
.expect("Shouldn't panic")
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sign a message with the wallet's private key.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `message` - The message to sign (arbitrary byte data)
|
||||||
|
/// * `address` - The address to use for signing (uses main address if None)
|
||||||
|
/// * `sign_with_view_key` - Whether to sign with view key instead of spend key (default: false)
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// A proof type prefix + base58 encoded signature
|
||||||
|
pub fn sign_message(
|
||||||
|
&mut self,
|
||||||
|
message: &str,
|
||||||
|
address: Option<&str>,
|
||||||
|
sign_with_view_key: bool,
|
||||||
|
) -> anyhow::Result<String> {
|
||||||
|
let_cxx_string!(message_cxx = message);
|
||||||
|
let_cxx_string!(address_cxx = address.unwrap_or(""));
|
||||||
|
|
||||||
|
let signature = ffi::signMessage(self.inner.pinned(), &message_cxx, &address_cxx, sign_with_view_key)
|
||||||
|
.context("Failed to sign message: FFI call failed with exception")?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
if signature.is_empty() {
|
||||||
|
self.check_error().context("Failed to sign message")?;
|
||||||
|
anyhow::bail!("Failed to sign message (no signature returned)");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(signature)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Safety: We check that it's never accessed outside the homethread at runtime.
|
/// Safety: We check that it's never accessed outside the homethread at runtime.
|
||||||
|
|
82
monero-sys/tests/sign_message.rs
Normal file
82
monero-sys/tests/sign_message.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
use monero_sys::{Daemon, WalletHandle};
|
||||||
|
|
||||||
|
const PLACEHOLDER_NODE: &str = "http://127.0.0.1:18081";
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_sign_message() {
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_env_filter("info,test=debug,sign_message=trace,monero_sys=trace")
|
||||||
|
.with_test_writer()
|
||||||
|
.init();
|
||||||
|
|
||||||
|
let temp_dir = tempfile::tempdir().unwrap();
|
||||||
|
let daemon = Daemon {
|
||||||
|
address: PLACEHOLDER_NODE.into(),
|
||||||
|
ssl: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let wallet_name = "test_signing_wallet";
|
||||||
|
let wallet_path = temp_dir.path().join(wallet_name).display().to_string();
|
||||||
|
|
||||||
|
tracing::info!("Creating wallet for message signing test");
|
||||||
|
let wallet = WalletHandle::open_or_create(
|
||||||
|
wallet_path,
|
||||||
|
daemon,
|
||||||
|
monero::Network::Stagenet,
|
||||||
|
false, // No background sync
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create wallet");
|
||||||
|
|
||||||
|
let main_address = wallet.main_address().await;
|
||||||
|
tracing::info!("Wallet main address: {}", main_address);
|
||||||
|
|
||||||
|
// Test message to sign
|
||||||
|
let test_message = "Hello, World! This is a test message for signing.";
|
||||||
|
|
||||||
|
tracing::info!("Testing message signing with spend key (default address)");
|
||||||
|
let signature_spend = wallet
|
||||||
|
.sign_message(test_message, None, false)
|
||||||
|
.await
|
||||||
|
.expect("Failed to sign message with spend key");
|
||||||
|
|
||||||
|
tracing::info!("Signature with spend key: {}", signature_spend);
|
||||||
|
assert!(!signature_spend.is_empty(), "Signature should not be empty");
|
||||||
|
assert!(signature_spend.len() > 10, "Signature should be reasonably long");
|
||||||
|
|
||||||
|
tracing::info!("Testing message signing with view key (default address)");
|
||||||
|
let signature_view = wallet
|
||||||
|
.sign_message(test_message, None, true)
|
||||||
|
.await
|
||||||
|
.expect("Failed to sign message with view key");
|
||||||
|
|
||||||
|
tracing::info!("Signature with view key: {}", signature_view);
|
||||||
|
assert!(!signature_view.is_empty(), "Signature should not be empty");
|
||||||
|
assert!(signature_view.len() > 10, "Signature should be reasonably long");
|
||||||
|
|
||||||
|
// Signatures should be different when using different keys
|
||||||
|
assert_ne!(signature_spend, signature_view, "Spend key and view key signatures should be different");
|
||||||
|
|
||||||
|
tracing::info!("Testing message signing with spend key (explicit address)");
|
||||||
|
let signature_explicit = wallet
|
||||||
|
.sign_message(test_message, Some(&main_address.to_string()), false)
|
||||||
|
.await
|
||||||
|
.expect("Failed to sign message with explicit address");
|
||||||
|
|
||||||
|
tracing::info!("Signature with explicit address: {}", signature_explicit);
|
||||||
|
assert!(!signature_explicit.is_empty(), "Signature should not be empty");
|
||||||
|
|
||||||
|
// When using the same key and same address (main address), signatures should be the same
|
||||||
|
assert_eq!(signature_spend, signature_explicit, "Signatures should be the same when using same key and address");
|
||||||
|
|
||||||
|
tracing::info!("Testing empty message signing");
|
||||||
|
let signature_empty = wallet
|
||||||
|
.sign_message("", None, false)
|
||||||
|
.await
|
||||||
|
.expect("Failed to sign empty message");
|
||||||
|
|
||||||
|
tracing::info!("Signature for empty message: {}", signature_empty);
|
||||||
|
assert!(!signature_empty.is_empty(), "Signature should not be empty even for empty message");
|
||||||
|
|
||||||
|
tracing::info!("All message signing tests passed!");
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue