use std::fmt::{Debug, Formatter};
use std::str::FromStr;
use ed25519_dalek::{Signer, SigningKey};
use stellar_strkey::ed25519::{PrivateKey, PublicKey};
use stellar_xdr::{SorobanAuthorizationEntry, TransactionEnvelope};
use super::xdr::{StellarXdrError, sign_auth_entries_for_address, sign_transaction_envelope};
#[derive(Clone)]
pub struct StellarSigner {
address: String,
public_key: [u8; 32],
signing_key: SigningKey,
}
impl Debug for StellarSigner {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StellarSigner")
.field("address", &self.address)
.finish_non_exhaustive()
}
}
impl StellarSigner {
pub fn from_secret(secret: impl AsRef<str>) -> Result<Self, StellarSignerError> {
let seed = PrivateKey::from_str(secret.as_ref())
.map_err(|e| StellarSignerError::Parse(e.to_string()))?;
let signing_key = SigningKey::from_bytes(&seed.0);
let verifying = signing_key.verifying_key();
let public_key = verifying.to_bytes();
let address = PublicKey(public_key).to_string().to_string();
Ok(Self {
address,
public_key,
signing_key,
})
}
#[must_use]
pub fn address(&self) -> &str {
&self.address
}
#[must_use]
pub const fn public_key_bytes(&self) -> &[u8; 32] {
&self.public_key
}
#[must_use]
pub fn sign_digest(&self, digest: &[u8; 32]) -> [u8; 64] {
self.signing_key.sign(digest).to_bytes()
}
pub fn sign_auth_entries(
&self,
auth: &mut [SorobanAuthorizationEntry],
passphrase: &str,
expiration_ledger: u32,
) -> Result<usize, StellarXdrError> {
sign_auth_entries_for_address(
auth,
&self.address,
passphrase,
expiration_ledger,
&self.public_key,
|digest| self.sign_digest(digest),
)
}
pub fn sign_envelope(
&self,
envelope: &mut TransactionEnvelope,
passphrase: &str,
) -> Result<(), StellarXdrError> {
sign_transaction_envelope(envelope, passphrase, &self.public_key, |digest| {
self.sign_digest(digest)
})
}
}
impl AsRef<Self> for StellarSigner {
fn as_ref(&self) -> &Self {
self
}
}
#[derive(Debug, thiserror::Error)]
pub enum StellarSignerError {
#[error("invalid stellar secret key: {0}")]
Parse(String),
}