Skip to content
Open
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
37 changes: 37 additions & 0 deletions wallet/core/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub use variants::*;
use crate::derivation::build_derivate_paths;
use crate::derivation::AddressDerivationManagerTrait;
use crate::imports::*;
use crate::message::{verify_message, PersonalMessage, SignMessageOptions};
use crate::storage::account::AccountSettings;
use crate::storage::AccountMetadata;
use crate::storage::{PrvKeyData, PrvKeyDataId};
Expand All @@ -30,6 +31,7 @@ use kaspa_bip32::{ChildNumber, ExtendedPrivateKey, PrivateKey};
use kaspa_consensus_client::UtxoEntry;
use kaspa_consensus_client::UtxoEntryReference;
use kaspa_wallet_keys::derivation::gen0::WalletDerivationManagerV0;
use secp256k1::PublicKey;
use workflow_core::abortable::Abortable;

/// Notification callback type used by [`Account::sweep`] and [`Account::send`].
Expand Down Expand Up @@ -506,6 +508,41 @@ pub trait Account: AnySync + Send + Sync + 'static {
Ok(ids)
}

async fn sign_message(
self: Arc<Self>,
message: &str,
address: &Address,
wallet_secret: Secret,
payment_secret: Option<Secret>,
no_aux_rand: bool,
) -> Result<(String, PublicKey)> {
let keydata = self.prv_key_data(wallet_secret).await?;
let signer = Arc::new(Signer::new(self.clone().as_dyn_arc(), keydata, payment_secret));
let options = SignMessageOptions { no_aux_rand };
signer.sign_message(message, address, options).await
}

async fn verify_message(
self: Arc<Self>,
message: &str,
signature: &str,
address: &Address,
wallet_secret: Secret,
payment_secret: Option<Secret>,
) -> Result<bool> {
let pm = PersonalMessage(message);
let mut signature_bytes = [0u8; 64];
let keydata = self.prv_key_data(wallet_secret).await?;
let private_keys = self.clone().create_address_private_keys(&keydata, &payment_secret, &[address])?;
if private_keys.is_empty() {
return Err(Error::custom(format!("No private key found for address: {}", address)));
}
let private_key = private_keys[0].1;
let public_key = PublicKey::from_secret_key_global(&private_key);
faster_hex::hex_decode(signature.as_bytes(), &mut signature_bytes)?;
Ok(verify_message(&pm, &signature_bytes.to_vec(), &public_key.into()).is_ok())
}

async fn get_utxos(self: Arc<Self>, addresses: Option<Vec<Address>>, min_amount_sompi: Option<u64>) -> Result<Vec<UtxoEntry>> {
let utxos = self.utxo_context().get_utxos(addresses, min_amount_sompi).await?;
Ok(utxos)
Expand Down
35 changes: 35 additions & 0 deletions wallet/core/src/api/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,41 @@ impl From<UtxoEntry> for UtxoEntryWrapper {
}
}

#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
#[serde(rename_all = "camelCase")]
pub struct AccountsSignMessageRequest {
pub account_id: AccountId,
pub message: String,
pub wallet_secret: Secret,
pub payment_secret: Option<Secret>,
pub no_aux_rand: Option<bool>,
pub address: Option<Address>,
}

#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
#[serde(rename_all = "camelCase")]
pub struct AccountsSignMessageResponse {
pub signature: String,
pub public_key: String,
}

#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
#[serde(rename_all = "camelCase")]
pub struct AccountsVerifyMessageRequest {
pub account_id: AccountId,
pub message: String,
pub signature: String,
pub wallet_secret: Secret,
pub payment_secret: Option<Secret>,
pub address: Option<Address>,
}

#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
#[serde(rename_all = "camelCase")]
pub struct AccountsVerifyMessageResponse {
pub verified: bool,
}

#[derive(Clone, Debug, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
#[serde(rename_all = "camelCase")]
pub struct AccountsTransferRequest {
Expand Down
19 changes: 19 additions & 0 deletions wallet/core/src/api/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,25 @@ pub trait WalletApi: Send + Sync + AnySync {
/// Get UTXOs for an account.
async fn accounts_get_utxos_call(self: Arc<Self>, request: AccountsGetUtxosRequest) -> Result<AccountsGetUtxosResponse>;

/// Wrapper around [`accounts_sign_message_call()`](Self::accounts_sign_message_call)
async fn accounts_sign_message(self: Arc<Self>, request: AccountsSignMessageRequest) -> Result<AccountsSignMessageResponse> {
self.accounts_sign_message_call(request).await
}

/// Sign a message.
async fn accounts_sign_message_call(self: Arc<Self>, request: AccountsSignMessageRequest) -> Result<AccountsSignMessageResponse>;

/// Wrapper around [`accounts_verify_message_call()`](Self::accounts_verify_message_call)
async fn accounts_verify_message(self: Arc<Self>, request: AccountsVerifyMessageRequest) -> Result<AccountsVerifyMessageResponse> {
self.accounts_verify_message_call(request).await
}

/// Verify a message.
async fn accounts_verify_message_call(
self: Arc<Self>,
request: AccountsVerifyMessageRequest,
) -> Result<AccountsVerifyMessageResponse>;

/// Transfer funds to another account. Returns an [`AccountsTransferResponse`]
/// struct that contains a [`GeneratorSummary`] as well `transaction_ids`
/// containing a list of submitted transaction ids. Unlike funds sent to an
Expand Down
4 changes: 4 additions & 0 deletions wallet/core/src/api/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ impl WalletApi for WalletClient {
AccountsPskbSign,
AccountsPskbBroadcast,
AccountsPskbSend,
AccountsSignMessage,
AccountsVerifyMessage,
AccountsGetUtxos,
AccountsTransfer,
AccountsEstimate,
Expand Down Expand Up @@ -188,6 +190,8 @@ impl WalletServer {
AccountsPskbSign,
AccountsPskbBroadcast,
AccountsPskbSend,
AccountsSignMessage,
AccountsVerifyMessage,
AccountsGetUtxos,
AccountsTransfer,
AccountsEstimate,
Expand Down
20 changes: 20 additions & 0 deletions wallet/core/src/tx/generator/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
//!

use crate::imports::*;
use crate::message::{sign_message, PersonalMessage, SignMessageOptions};
use kaspa_bip32::PrivateKey;
use kaspa_consensus_core::{sign::sign_with_multiple_v2, tx::SignableTransaction};
use secp256k1::PublicKey;

pub trait SignerT: Send + Sync + 'static {
fn try_sign(&self, transaction: SignableTransaction, addresses: &[Address]) -> Result<SignableTransaction>;
Expand Down Expand Up @@ -46,6 +48,24 @@ impl Signer {

Ok(())
}

pub async fn sign_message(
&self,
message: &str,
address: &Address,
sign_options: SignMessageOptions,
) -> Result<(String, PublicKey)> {
let private_keys =
self.inner.account.clone().create_address_private_keys(&self.inner.keydata, &self.inner.payment_secret, &[address])?;
if private_keys.is_empty() {
return Err(Error::custom(format!("No private key found for address: {}", address)));
}
let private_key = private_keys[0].1;
let privkey_bytes = private_key.to_bytes();
let pm = PersonalMessage(message);
let sig_vec = sign_message(&pm, &privkey_bytes, &sign_options)?;
Ok((faster_hex::hex_string(sig_vec.as_slice()), PublicKey::from_secret_key_global(&private_key)))
}
}

impl SignerT for Signer {
Expand Down
28 changes: 28 additions & 0 deletions wallet/core/src/wallet/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,34 @@ impl WalletApi for super::Wallet {
Ok(AccountsPskbBroadcastResponse { transaction_ids })
}

async fn accounts_sign_message_call(self: Arc<Self>, request: AccountsSignMessageRequest) -> Result<AccountsSignMessageResponse> {
let AccountsSignMessageRequest { account_id, message, wallet_secret, payment_secret, no_aux_rand, address } = request;

let guard = self.guard();
let guard = guard.lock().await;

let account = self.get_account_by_id(&account_id, &guard).await?.ok_or(Error::AccountNotFound(account_id))?;
let address = address.unwrap_or(account.receive_address()?);
let (signature, public_key) =
account.sign_message(&message, &address, wallet_secret, payment_secret, no_aux_rand.unwrap_or(false)).await?;
Ok(AccountsSignMessageResponse { signature, public_key: public_key.to_string() })
}

async fn accounts_verify_message_call(
self: Arc<Self>,
request: AccountsVerifyMessageRequest,
) -> Result<AccountsVerifyMessageResponse> {
let AccountsVerifyMessageRequest { account_id, message, signature, wallet_secret, payment_secret, address } = request;

let guard = self.guard();
let guard = guard.lock().await;

let account = self.get_account_by_id(&account_id, &guard).await?.ok_or(Error::AccountNotFound(account_id))?;
let address = address.unwrap_or(account.receive_address()?);
let verified = account.verify_message(&message, &signature, &address, wallet_secret, payment_secret).await?;
Ok(AccountsVerifyMessageResponse { verified })
}

async fn accounts_get_utxos_call(self: Arc<Self>, request: AccountsGetUtxosRequest) -> Result<AccountsGetUtxosResponse> {
let AccountsGetUtxosRequest { account_id, addresses, min_amount_sompi } = request;
let guard = self.guard();
Expand Down
110 changes: 110 additions & 0 deletions wallet/core/src/wasm/api/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1647,6 +1647,116 @@ try_from! ( args: AccountsPskbSendResponse, IAccountsPskbSendResponse, {

// ---

declare! {
IAccountsSignMessageRequest,
r#"
/**
*
*
* @category Wallet API
*/
export interface IAccountsSignMessageRequest {
accountId : HexString;
message : string;
walletSecret : string;
paymentSecret? : string;
noAuxRand? : boolean;
address? : Address | string;
}
"#,
}

try_from! ( args: IAccountsSignMessageRequest, AccountsSignMessageRequest, {
let account_id = args.get_account_id("accountId")?;
let message = args.get_string("message")?;
let wallet_secret = args.get_secret("walletSecret")?;
let payment_secret = args.try_get_secret("paymentSecret")?;
let no_aux_rand = args.get_bool("noAuxRand").ok();
let address = match args.try_get_value("address")? {
Some(v) => Some(Address::try_cast_from(&v)?.into_owned()),
None => None,
};
Ok(AccountsSignMessageRequest { account_id, message, wallet_secret, payment_secret, no_aux_rand, address })
});

declare! {
IAccountsSignMessageResponse,
r#"
/**
*
*
* @category Wallet API
*/
export interface IAccountsSignMessageResponse {
signature : string;
publicKey : string;
}
"#,
}

try_from! ( args: AccountsSignMessageResponse, IAccountsSignMessageResponse, {
let response = IAccountsSignMessageResponse::default();
response.set("signature", &args.signature.into())?;
response.set("publicKey", &args.public_key.into())?;
Ok(response)
});

// ---

declare! {
IAccountsVerifyMessageRequest,
r#"
/**
*
*
* @category Wallet API
*/
export interface IAccountsVerifyMessageRequest {
accountId : HexString;
message : string;
signature : string;
walletSecret : string;
paymentSecret? : string;
address? : Address | string;
}
"#,
}

try_from! ( args: IAccountsVerifyMessageRequest, AccountsVerifyMessageRequest, {
let account_id = args.get_account_id("accountId")?;
let message = args.get_string("message")?;
let signature = args.get_string("signature")?;
let wallet_secret = args.get_secret("walletSecret")?;
let payment_secret = args.try_get_secret("paymentSecret")?;
let address = match args.try_get_value("address")? {
Some(v) => Some(Address::try_cast_from(&v)?.into_owned()),
None => None,
};
Ok(AccountsVerifyMessageRequest { account_id, message, signature, wallet_secret, payment_secret, address })
});

declare! {
IAccountsVerifyMessageResponse,
r#"
/**
*
*
* @category Wallet API
*/
export interface IAccountsVerifyMessageResponse {
verified : boolean;
}
"#,
}

try_from! ( args: AccountsVerifyMessageResponse, IAccountsVerifyMessageResponse, {
let response = IAccountsVerifyMessageResponse::default();
response.set("verified", &args.verified.into())?;
Ok(response)
});

// ---

declare! {
IAccountsGetUtxosRequest,
r#"
Expand Down
2 changes: 2 additions & 0 deletions wallet/core/src/wasm/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ declare_wasm_handlers!([
AccountsPskbSign,
AccountsPskbBroadcast,
AccountsPskbSend,
AccountsSignMessage,
AccountsVerifyMessage,
AccountsGetUtxos,
AccountsTransfer,
AccountsEstimate,
Expand Down
Loading