mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-24 05:59:42 -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
|
|
@ -270,6 +270,14 @@ pub mod ffi {
|
|||
self: Pin<&mut Wallet>,
|
||||
tx: *mut PendingTransaction,
|
||||
) -> 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
|
||||
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 {
|
||||
|
@ -1727,6 +1751,36 @@ impl FfiWallet {
|
|||
.expect("Shouldn't panic")
|
||||
.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.
|
||||
|
|
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