#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use alloc::{string::String, vec::Vec};
mod error;
pub use error::SignError;
use ripemd::{Digest as _, Ripemd160};
use sha2::{Digest, Sha256};
use signer_primitives::Secp256k1Signer;
pub use signer_primitives::{self, Sign, SignExt, SignOutput};
pub struct Signer {
inner: Secp256k1Signer,
}
impl core::fmt::Debug for Signer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Signer")
.field("key", &"[REDACTED]")
.finish()
}
}
impl Signer {
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, SignError> {
Ok(Self {
inner: Secp256k1Signer::from_bytes(bytes)?,
})
}
pub fn from_hex(hex_str: &str) -> Result<Self, SignError> {
Ok(Self {
inner: Secp256k1Signer::from_hex(hex_str)?,
})
}
#[cfg(feature = "getrandom")]
#[must_use]
pub fn random() -> Self {
Self {
inner: Secp256k1Signer::random(),
}
}
#[must_use]
#[allow(
clippy::indexing_slicing,
reason = "SHA-256 output is always 32 bytes, slicing first 4 is safe"
)]
pub fn address(&self) -> String {
let pubkey = self.inner.compressed_public_key();
let sha = Sha256::digest(&pubkey);
let hash160 = Ripemd160::digest(sha);
let mut payload = Vec::with_capacity(25);
payload.push(0x00);
payload.extend_from_slice(&hash160);
let checksum = Sha256::digest(Sha256::digest(&payload));
payload.extend_from_slice(&checksum[..4]);
bs58::encode(&payload).into_string()
}
#[must_use]
pub fn public_key_bytes(&self) -> Vec<u8> {
self.inner.compressed_public_key()
}
pub fn sign_hash(&self, hash: &[u8]) -> Result<SignOutput, SignError> {
Ok(self.inner.sign_prehash_recoverable(hash)?)
}
pub fn sign_transaction(&self, tx_bytes: &[u8]) -> Result<SignOutput, SignError> {
let hash = Sha256::digest(Sha256::digest(tx_bytes));
self.sign_hash(&hash)
}
pub fn sign_message(&self, message: &[u8]) -> Result<SignOutput, SignError> {
let hash = Sha256::digest(Sha256::digest(message));
self.sign_hash(&hash)
}
}
signer_primitives::impl_sign_delegate!();
#[cfg(feature = "kobe")]
impl Signer {
pub fn from_derived(account: &kobe_spark::DerivedAccount) -> Result<Self, SignError> {
Self::from_hex(&account.private_key)
}
}
#[cfg(test)]
mod tests;