use anyswap_core::{
secp,
types::SwapNetwork,
wallet::{Keyring, KeyringError},
};
use bip39::Mnemonic;
use bitcoin::{
hashes::{Hash, sha256},
secp256k1::{PublicKey, SecretKey},
};
use thiserror::Error;
use uuid::Uuid;
const BRANCH_XPUB: u32 = 0;
const BRANCH_KEYS: u32 = 1;
const BRANCH_SOURCE_BLINDING: u32 = 2;
const BRANCH_PREIMAGES: u32 = 3;
const BRANCH_DEST_BLINDING: u32 = 4;
pub trait SwapWallet {
type Error: std::fmt::Display;
fn network(&self) -> SwapNetwork;
fn identity_pubkey(&self) -> Result<PublicKey, Self::Error>;
fn pubkey(&self, swap_id: &Uuid) -> Result<PublicKey, Self::Error>;
fn privkey(&self, swap_id: &Uuid) -> Result<SecretKey, Self::Error>;
fn preimage(&self, swap_id: &Uuid) -> Result<[u8; 32], Self::Error>;
fn source_blinding_pubkey(&self, swap_id: &Uuid) -> Result<PublicKey, Self::Error>;
fn source_blinding_privkey(&self, swap_id: &Uuid) -> Result<SecretKey, Self::Error>;
fn dest_blinding_pubkey(&self, swap_id: &Uuid) -> Result<PublicKey, Self::Error>;
fn dest_blinding_privkey(&self, swap_id: &Uuid) -> Result<SecretKey, Self::Error>;
}
pub trait SwapWalletExt: SwapWallet {
fn user_id(&self) -> Result<String, Self::Error> {
Ok(sha256::Hash::hash(&self.identity_pubkey()?.serialize()).to_string())
}
fn payment_hash(&self, swap_id: &Uuid) -> Result<[u8; 32], Self::Error> {
let preimage = self.preimage(swap_id)?;
Ok(*sha256::Hash::hash(&preimage).as_byte_array())
}
}
impl<W: SwapWallet + ?Sized> SwapWalletExt for W {}
#[derive(Debug, Error)]
pub enum WalletError {
#[error("mnemonic: {0}")]
Mnemonic(#[from] bip39::Error),
#[error("keyring: {0}")]
Keyring(#[from] KeyringError),
}
#[derive(Clone)]
pub struct Wallet {
keyring: Keyring,
network: SwapNetwork,
}
impl Wallet {
pub fn generate(network: SwapNetwork) -> Result<(Self, Mnemonic), WalletError> {
let mnemonic = Self::generate_mnemonic(24)?;
let wallet = Self::from_mnemonic(&mnemonic, network)?;
Ok((wallet, mnemonic))
}
pub fn generate_mnemonic(word_count: u8) -> Result<Mnemonic, WalletError> {
Ok(Mnemonic::generate(word_count as usize)?)
}
pub fn from_mnemonic(mnemonic: &Mnemonic, network: SwapNetwork) -> Result<Self, WalletError> {
let seed = mnemonic.to_seed("");
let keyring = Keyring::from_seed(
&seed,
network,
&[
BRANCH_XPUB,
BRANCH_KEYS,
BRANCH_SOURCE_BLINDING,
BRANCH_PREIMAGES,
BRANCH_DEST_BLINDING,
],
)?;
Ok(Self { keyring, network })
}
pub fn identity_privkey(&self) -> Result<SecretKey, WalletError> {
Ok(self.keyring.branch_privkey(BRANCH_XPUB)?)
}
}
impl SwapWallet for Wallet {
type Error = WalletError;
fn network(&self) -> SwapNetwork {
self.network
}
fn identity_pubkey(&self) -> Result<PublicKey, WalletError> {
Ok(self.keyring.branch_pubkey(BRANCH_XPUB)?)
}
fn pubkey(&self, swap_id: &Uuid) -> Result<PublicKey, WalletError> {
Ok(self.privkey(swap_id)?.public_key(secp()))
}
fn privkey(&self, swap_id: &Uuid) -> Result<SecretKey, WalletError> {
Ok(self.keyring.swap_key(BRANCH_KEYS, swap_id)?)
}
fn preimage(&self, swap_id: &Uuid) -> Result<[u8; 32], WalletError> {
Ok(self
.keyring
.swap_key(BRANCH_PREIMAGES, swap_id)?
.secret_bytes())
}
fn source_blinding_pubkey(&self, swap_id: &Uuid) -> Result<PublicKey, WalletError> {
Ok(self.source_blinding_privkey(swap_id)?.public_key(secp()))
}
fn source_blinding_privkey(&self, swap_id: &Uuid) -> Result<SecretKey, WalletError> {
Ok(self.keyring.swap_key(BRANCH_SOURCE_BLINDING, swap_id)?)
}
fn dest_blinding_pubkey(&self, swap_id: &Uuid) -> Result<PublicKey, WalletError> {
Ok(self.dest_blinding_privkey(swap_id)?.public_key(secp()))
}
fn dest_blinding_privkey(&self, swap_id: &Uuid) -> Result<SecretKey, WalletError> {
Ok(self.keyring.swap_key(BRANCH_DEST_BLINDING, swap_id)?)
}
}