| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900 |
- // TODO: This module needs cleanup related to PublicKey/SecretKey types.
- use std::{
- cmp::max,
- collections::BTreeMap,
- convert::{From, TryFrom, TryInto},
- fmt,
- ops::Add,
- str::FromStr,
- time::{Duration, Instant},
- };
- use anyhow::Context;
- use async_executor::Executor;
- use async_std::sync::{Arc, Mutex};
- use async_trait::async_trait;
- use bdk::electrum_client::{
- Client as ElectrumClient, ElectrumApi, GetBalanceRes, GetHistoryRes, HeaderNotification,
- };
- use bitcoin::{
- blockdata::{
- script::{Builder, Script},
- transaction::{OutPoint, SigHashType, Transaction, TxIn, TxOut},
- },
- consensus::encode::serialize_hex,
- hash_types::PubkeyHash as BtcPubKeyHash,
- network::constants::Network,
- util::{
- address::Address,
- ecdsa::{PrivateKey as BtcPrivKey, PublicKey as BtcPubKey},
- psbt::serialize::Serialize,
- },
- };
- use log::*;
- use secp256k1::{
- constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE},
- key::{PublicKey, SecretKey},
- rand::rngs::OsRng,
- All, Message as BtcMessage, Secp256k1,
- };
- use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
- use crate::{
- crypto::keypair::PublicKey as DrkPublicKey,
- serial::{deserialize, serialize, Decodable, Encodable},
- util::{generate_id2, NetworkName},
- Error, Result,
- };
- // Swap out these types for any future non bitcoin-rs types
- pub type PubAddress = Address;
- pub type PubKey = BtcPubKey;
- pub type PrivKey = BtcPrivKey;
- const KEYPAIR_LENGTH: usize = SECRET_KEY_SIZE + PUBLIC_KEY_SIZE;
- #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
- pub struct BlockHeight(u32);
- impl From<BlockHeight> for u32 {
- fn from(height: BlockHeight) -> Self {
- height.0
- }
- }
- impl TryFrom<HeaderNotification> for BlockHeight {
- type Error = BtcFailed;
- fn try_from(value: HeaderNotification) -> BtcResult<Self> {
- Ok(Self(value.height.try_into().context("Failed to fit usize into u32")?))
- }
- }
- impl Add<u32> for BlockHeight {
- type Output = BlockHeight;
- fn add(self, rhs: u32) -> Self::Output {
- BlockHeight(self.0 + rhs)
- }
- }
- #[derive(Debug, Clone, Copy, PartialEq)]
- pub enum ExpiredTimelocks {
- None,
- Cancel,
- Punish,
- }
- #[derive(Clone, Debug, PartialEq)]
- pub struct Keypair {
- secret: SecretKey,
- public: PublicKey,
- context: Secp256k1<All>,
- }
- impl Keypair {
- pub fn new() -> Self {
- let secp = Secp256k1::new();
- let mut rng = OsRng::new().expect("OsRng");
- let (secret, public) = secp.generate_keypair(&mut rng);
- Self { secret, public, context: secp }
- }
- pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
- let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
- bytes[..SECRET_KEY_SIZE].copy_from_slice(self.secret.as_ref());
- bytes[SECRET_KEY_SIZE..].copy_from_slice(&self.public.serialize());
- bytes
- }
- pub fn from_bytes(bytes: &[u8]) -> BtcResult<Keypair> {
- if bytes.len() != KEYPAIR_LENGTH {
- return Err(BtcFailed::KeypairError("Not right size".to_string()))
- }
- let secp = Secp256k1::new();
- let secret = SecretKey::from_slice(&bytes[..SECRET_KEY_SIZE])?;
- let public = PublicKey::from_slice(&bytes[SECRET_KEY_SIZE..])?;
- Ok(Keypair { secret, public, context: secp })
- }
- fn secret(&self) -> SecretKey {
- self.secret
- }
- pub fn pubkey(&self) -> PublicKey {
- self.public
- }
- pub fn as_tuple(&self) -> (SecretKey, PublicKey) {
- (self.secret, self.public)
- }
- }
- impl Default for Keypair {
- fn default() -> Self {
- Self::new()
- }
- }
- #[derive(Clone)]
- pub struct Account {
- keypair: Arc<Keypair>,
- btc_privkey: BtcPrivKey,
- pub btc_pubkey: BtcPubKey,
- pub address: Address,
- pub script_pubkey: Script,
- pub network: Network,
- }
- impl Account {
- pub fn new(keypair: &Keypair, network: Network) -> Self {
- let (secret_key, _public_key) = keypair.as_tuple();
- let btc_privkey = BtcPrivKey::new(secret_key, network);
- let btc_pubkey = btc_privkey.public_key(&keypair.context);
- let address = Account::derive_btc_address(btc_pubkey, network);
- let script_pubkey = address.script_pubkey();
- Self {
- keypair: Arc::new(keypair.clone()),
- btc_privkey,
- btc_pubkey,
- address,
- script_pubkey,
- network,
- }
- }
- pub fn priv_from_secret(keypair: &Keypair, network: Network) -> BtcPrivKey {
- BtcPrivKey::new(keypair.secret(), network)
- }
- pub fn btcpub_from_keypair(keypair: &Keypair) -> BtcPubKey {
- BtcPubKey::new(keypair.public)
- }
- pub fn btc_privkey(&self) -> &BtcPrivKey {
- &self.btc_privkey
- }
- pub fn btc_pubkey(&self) -> &BtcPubKey {
- &self.btc_pubkey
- }
- pub fn btc_pubkey_hash(&self) -> BtcPubKeyHash {
- self.btc_pubkey.pubkey_hash()
- }
- pub fn derive_btc_script_pubkey(pubkey: PublicKey, network: Network) -> Script {
- let btc_pubkey = BtcPubKey::new(pubkey);
- let address = Address::p2pkh(&btc_pubkey, network);
- address.script_pubkey()
- }
- pub fn derive_btc_pubkey(pubkey: PublicKey) -> BtcPubKey {
- BtcPubKey::new(pubkey)
- }
- pub fn derive_btc_address(btc_pubkey: BtcPubKey, network: Network) -> Address {
- Address::p2pkh(&btc_pubkey, network)
- }
- pub fn derive_script(btc_pubkey_hash: BtcPubKeyHash) -> Script {
- Script::new_p2pkh(&btc_pubkey_hash)
- }
- }
- fn print_status_change(
- script: &Script,
- old: Option<ScriptStatus>,
- new: ScriptStatus,
- ) -> ScriptStatus {
- match (old, new) {
- (None, new_status) => {
- debug!(target: "BTC BRIDGE", "Found relevant script: {:?}, Status: {:?}", script, new_status);
- }
- (Some(old_status), new_status) if old_status != new_status => {
- debug!(target: "BTC BRIDGE", "Script status changed: {:?}, to {} from {}", script, new_status, old_status);
- }
- _ => {}
- }
- new
- }
- fn sync_interval(avg_block_time: Duration) -> Duration {
- max(avg_block_time / 10, Duration::from_secs(1))
- }
- pub struct Client {
- electrum: ElectrumClient,
- subscriptions: Vec<Script>,
- latest_block_height: BlockHeight,
- last_sync: Instant,
- sync_interval: Duration,
- script_history: BTreeMap<Script, Vec<GetHistoryRes>>,
- }
- impl Client {
- pub fn new(electrum_url: &str) -> BtcResult<Self> {
- let config = bdk::electrum_client::ConfigBuilder::default().retry(5).build();
- let _client = ElectrumClient::from_config(electrum_url, config)?;
- let electrum = ElectrumClient::new(electrum_url)
- .map_err(|err| crate::Error::from(super::BtcFailed::from(err)))?;
- let latest_block = electrum.block_headers_subscribe()?;
- //testnet avg block time
- let interval = sync_interval(Duration::from_secs(300));
- Ok(Self {
- electrum,
- subscriptions: Vec::new(),
- latest_block_height: BlockHeight::try_from(latest_block)
- .map_err(|_| crate::Error::TryFromError)?,
- last_sync: Instant::now(),
- sync_interval: interval,
- script_history: Default::default(),
- })
- }
- fn update_state(&mut self) -> Result<()> {
- let now = Instant::now();
- if now < self.last_sync + self.sync_interval {
- return Ok(())
- }
- self.last_sync = now;
- self.update_latest_block()?;
- self.update_script_histories()?;
- Ok(())
- }
- fn update_latest_block(&mut self) -> BtcResult<()> {
- let latest_block = self.electrum.block_headers_subscribe()?;
- let latest_block_height = BlockHeight::try_from(latest_block)?;
- if latest_block_height > self.latest_block_height {
- debug!( target: "BTC BRIDGE", "{} {}",
- u32::from(latest_block_height),
- "Got notification for new block"
- );
- self.latest_block_height = latest_block_height;
- }
- Ok(())
- }
- fn update_script_histories(&mut self) -> BtcResult<()> {
- let histories = self.electrum.batch_script_get_history(self.script_history.keys())?;
- if histories.len() != self.script_history.len() {
- debug!(
- "Expected {} history entries, received {}",
- self.script_history.len(),
- histories.len()
- );
- }
- let scripts = self.script_history.keys().cloned();
- let histories = histories.into_iter();
- self.script_history = scripts.zip(histories).collect::<BTreeMap<_, _>>();
- Ok(())
- }
- pub fn status_of_script(&mut self, script: Script) -> BtcResult<ScriptStatus> {
- if !self.script_history.contains_key(&script) {
- self.script_history.insert(script.clone(), vec![]);
- }
- self.update_state()?;
- let history = self.script_history.entry(script).or_default();
- match history.as_slice() {
- [] => Ok(ScriptStatus::Unseen),
- [_remaining @ .., last] => {
- if last.height <= 0 {
- Ok(ScriptStatus::InMempool)
- } else {
- Ok(ScriptStatus::Confirmed(Confirmed::from_inclusion_and_latest_block(
- u32::try_from(last.height).map_err(|_| crate::Error::TryFromError)?,
- u32::from(self.latest_block_height),
- )))
- }
- }
- }
- }
- }
- pub struct BtcClient {
- main_account: Account,
- client: Arc<Mutex<Client>>,
- notify_channel:
- (async_channel::Sender<TokenNotification>, async_channel::Receiver<TokenNotification>),
- network: Network,
- }
- impl BtcClient {
- pub async fn new(main_keypair: Keypair, network: &str) -> Result<Arc<Self>> {
- let notify_channel = async_channel::unbounded();
- let (network, url) = match network {
- "mainnet" => (Network::Bitcoin, "ssl://electrum.blockstream.info:50002"),
- "testnet" => (Network::Testnet, "ssl://electrum.blockstream.info:60002"),
- _ => return Err(Error::NotSupportedNetwork),
- };
- let main_account = Account::new(&main_keypair, network);
- info!(target: "BTC BRIDGE", "Main BTC Address: {}", main_account.address.to_string());
- Ok(Arc::new(Self {
- main_account,
- client: Arc::new(Mutex::new(Client::new(url)?)),
- notify_channel,
- network,
- }))
- }
- async fn handle_subscribe_request(
- self: Arc<Self>,
- btc_keys: Account,
- drk_pub_key: DrkPublicKey,
- ) -> BtcResult<()> {
- let client = self.client.clone();
- let keys_clone = btc_keys.clone();
- let script = keys_clone.script_pubkey;
- if client.lock().await.subscriptions.contains(&script) {
- return Ok(())
- } else {
- client.lock().await.subscriptions.push(script.clone());
- }
- //Fetch any current balance
- let prev_balance = client.lock().await.electrum.script_get_balance(&script)?;
- let mut last_status = None;
- loop {
- async_std::task::sleep(Duration::from_secs(5)).await;
- let new_status = match client.lock().await.status_of_script(script.clone()) {
- Ok(new_status) => new_status,
- Err(error) => {
- debug!(target: "BTC BRIDGE", "Failed to get status of script: {:#}", error);
- return Err(BtcFailed::BtcError("Failed to get status of script".to_string()))
- }
- };
- last_status = Some(print_status_change(&script, last_status, new_status));
- match new_status {
- ScriptStatus::Unseen => continue,
- ScriptStatus::InMempool => break,
- ScriptStatus::Confirmed(inner) => {
- //Only break when confirmations happen
- let confirmations = inner.confirmations();
- if confirmations > 1 {
- break
- }
- }
- }
- }
- let index = &mut client.lock().await.subscriptions.iter().position(|p| p == &script);
- if let Some(ind) = index {
- trace!(target: "BTC BRIDGE", "Removing subscription from list");
- let _ = &mut client.lock().await.subscriptions.remove(*ind);
- }
- let cur_balance: GetBalanceRes =
- client.lock().await.electrum.script_get_balance(&script)?;
- let send_notification = self.notify_channel.0.clone();
- //FIXME: dev
- if cur_balance.unconfirmed < prev_balance.unconfirmed {
- return Err(BtcFailed::Notification("New balance is less than previous balance".into()))
- }
- //Just check unconfirmed for now
- let amnt = cur_balance.confirmed - prev_balance.confirmed;
- let ui_amnt = amnt;
- send_notification
- .send(TokenNotification {
- network: NetworkName::Bitcoin,
- // is btc an acceptable token name?
- token_id: generate_id2(
- "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
- &NetworkName::Bitcoin,
- )?,
- drk_pub_key,
- received_balance: amnt as u64,
- decimals: 8,
- })
- .await
- .map_err(Error::from)?;
- info!(target: "BTC BRIDGE", "Received {} btc", ui_amnt);
- let _ = self.send_btc_to_main_wallet(amnt as u64, btc_keys).await;
- Ok(())
- }
- async fn send_btc_to_main_wallet(
- self: Arc<Self>,
- amount: u64,
- btc_keys: Account,
- ) -> BtcResult<()> {
- info!(target: "BTC BRIDGE", "Sending {} BTC to main wallet", amount);
- let client = self.client.lock().await;
- let electrum = &client.electrum;
- let keys_clone = btc_keys.clone();
- let script = keys_clone.script_pubkey;
- let utxo = electrum.script_list_unspent(&script)?;
- let mut inputs = Vec::new();
- let mut amounts: u64 = 0;
- for tx in utxo {
- let tx_in = TxIn {
- previous_output: OutPoint { txid: tx.tx_hash, vout: tx.tx_pos as u32 },
- sequence: 0xffffffff,
- witness: Vec::new(),
- script_sig: Script::new(),
- };
- inputs.push(tx_in);
- amounts += tx.value;
- }
- let main_script_pubkey = self.main_account.script_pubkey.clone();
- //TODO: Change to PSBT
- let transaction = Transaction {
- input: inputs.clone(),
- output: vec![TxOut { script_pubkey: main_script_pubkey.clone(), value: amounts }],
- lock_time: 0,
- version: 2,
- };
- let tx_size = transaction.get_size();
- let fee_per_kb = electrum.estimate_fee(1)?;
- let _fee = tx_size as f64 * fee_per_kb * 100000_f64;
- let transaction = Transaction {
- input: inputs,
- output: vec![TxOut {
- script_pubkey: main_script_pubkey,
- // TODO: calculate fee properly above
- value: amounts - 400,
- }],
- lock_time: 0,
- version: 2,
- };
- let _txid = transaction.txid();
- let signed_tx = sign_transaction(
- transaction,
- script,
- btc_keys.keypair.secret,
- btc_keys.btc_pubkey,
- &btc_keys.keypair.context,
- )?;
- let _txid = signed_tx.txid();
- let _serialized_tx = serialize(&signed_tx);
- info!(target: "BTC BRIDGE", "Signed tx: {:?}",
- serialize_hex(&signed_tx));
- let txid = electrum.transaction_broadcast_raw(&signed_tx.serialize().to_vec())?;
- info!(target: "BTC BRIDGE", "Sent {} satoshi to main wallet, txid: {}", amount, txid);
- Ok(())
- }
- }
- #[async_trait]
- impl NetworkClient for BtcClient {
- async fn subscribe(
- self: Arc<Self>,
- drk_pub_key: DrkPublicKey,
- _mint: Option<String>,
- executor: Arc<Executor<'_>>,
- ) -> Result<TokenSubscribtion> {
- // Generate bitcoin keys
- let keypair = Keypair::new();
- let btc_keys = Account::new(&keypair, self.network);
- let private_key = serialize(&keypair);
- let public_key = btc_keys.address.to_string();
- // start scheduler for checking balance
- trace!(target: "BRIDGE BITCOIN", "Subscribing for deposit");
- executor
- .spawn(async move {
- let result = self.handle_subscribe_request(btc_keys, drk_pub_key).await;
- if let Err(e) = result {
- error!(target: "BTC BRIDGE SUBSCRIPTION","{}", e.to_string());
- }
- })
- .detach();
- Ok(TokenSubscribtion { private_key, public_key })
- }
- async fn subscribe_with_keypair(
- self: Arc<Self>,
- private_key: Vec<u8>,
- _public_key: Vec<u8>,
- drk_pub_key: DrkPublicKey,
- _mint: Option<String>,
- executor: Arc<Executor<'_>>,
- ) -> Result<String> {
- let keypair: Keypair = deserialize(&private_key)?;
- let btc_keys = Account::new(&keypair, self.network);
- let public_key = btc_keys.address.to_string();
- executor
- .spawn(async move {
- let result = self.handle_subscribe_request(btc_keys, drk_pub_key).await;
- if let Err(e) = result {
- error!(target: "BTC BRIDGE SUBSCRIPTION","{}", e.to_string());
- }
- })
- .detach();
- Ok(public_key)
- }
- async fn get_notifier(self: Arc<Self>) -> Result<async_channel::Receiver<TokenNotification>> {
- Ok(self.notify_channel.1.clone())
- }
- async fn send(
- self: Arc<Self>,
- address: Vec<u8>,
- _mint: Option<String>,
- amount: u64,
- ) -> Result<()> {
- // address is not a btc address, so derive the btc address
- let electrum = &self.client.lock().await.electrum;
- let public_key = deserialize(&address)?;
- let script_pubkey = Account::derive_btc_script_pubkey(public_key, self.network);
- let main_script_pubkey = &self.main_account.script_pubkey;
- let main_utxo = electrum
- .script_list_unspent(main_script_pubkey)
- .map_err(|e| Error::from(BtcFailed::from(e)))?;
- let transaction = Transaction {
- input: vec![TxIn {
- previous_output: OutPoint {
- txid: main_utxo[0].tx_hash,
- vout: main_utxo[0].tx_pos as u32,
- },
- sequence: 0xffffffff,
- witness: Vec::new(),
- script_sig: Script::new(),
- }],
- output: vec![TxOut {
- script_pubkey: script_pubkey.clone(),
- // TODO: Calculate fees
- value: amount - 300,
- }],
- lock_time: 0,
- version: 2,
- };
- let signed_tx = sign_transaction(
- transaction,
- script_pubkey,
- self.main_account.keypair.secret,
- self.main_account.btc_pubkey,
- &self.main_account.keypair.context,
- )?;
- let txid = electrum
- .transaction_broadcast_raw(&signed_tx.serialize().to_vec())
- .map_err(|e| Error::from(BtcFailed::from(e)))?;
- info!(target: "BTC BRIDGE", "Sent {} satoshi to external wallet, txid: {}", amount, txid);
- Ok(())
- }
- }
- pub fn sign_transaction(
- tx: Transaction,
- script_pubkey: Script,
- priv_key: SecretKey,
- pub_key: BtcPubKey,
- curve: &Secp256k1<All>,
- ) -> BtcResult<Transaction> {
- let mut signed_inputs: Vec<TxIn> = Vec::new();
- for (i, unsigned_input) in tx.input.iter().enumerate() {
- let sighash = tx.signature_hash(i, &script_pubkey, SigHashType::All as u32);
- let msg = BtcMessage::from_slice(sighash.as_ref())?;
- let signature = curve.sign(&msg, &priv_key);
- let byte_signature = &signature.serialize_der();
- let mut with_hashtype = byte_signature.to_vec();
- with_hashtype.push(SigHashType::All as u8);
- let redeem_script =
- Builder::new().push_slice(with_hashtype.as_slice()).push_key(&pub_key).into_script();
- signed_inputs.push(TxIn {
- previous_output: unsigned_input.previous_output,
- script_sig: redeem_script,
- sequence: unsigned_input.sequence,
- witness: unsigned_input.witness.clone(),
- });
- }
- Ok(Transaction {
- version: tx.version,
- lock_time: tx.lock_time,
- input: signed_inputs,
- output: tx.output,
- })
- }
- #[derive(Debug, Copy, Clone, PartialEq)]
- pub enum ScriptStatus {
- Unseen,
- InMempool,
- Confirmed(Confirmed),
- }
- impl ScriptStatus {
- pub fn from_confirmations(confirmations: u32) -> Self {
- match confirmations {
- 0 => Self::InMempool,
- confirmations => Self::Confirmed(Confirmed::new(confirmations - 1)),
- }
- }
- }
- #[derive(Debug, Copy, Clone, PartialEq)]
- pub struct Confirmed {
- depth: u32,
- }
- impl Confirmed {
- pub fn new(depth: u32) -> Self {
- Self { depth }
- }
- pub fn from_inclusion_and_latest_block(inclusion_height: u32, latest_block: u32) -> Self {
- let depth = latest_block.saturating_sub(inclusion_height);
- Self { depth }
- }
- pub fn confirmations(&self) -> u32 {
- self.depth + 1
- }
- pub fn meets_target<T>(&self, target: T) -> bool
- where
- u32: PartialOrd<T>,
- {
- self.confirmations() >= target
- }
- }
- impl ScriptStatus {
- // Check if the script has any confirmations.
- pub fn is_confirmed(&self) -> bool {
- matches!(self, ScriptStatus::Confirmed(_))
- }
- // Check if the script has met the given confirmation target.
- pub fn is_confirmed_with<T>(&self, target: T) -> bool
- where
- u32: PartialOrd<T>,
- {
- match self {
- ScriptStatus::Confirmed(inner) => inner.meets_target(target),
- _ => false,
- }
- }
- pub fn has_been_seen(&self) -> bool {
- matches!(self, ScriptStatus::InMempool | ScriptStatus::Confirmed(_))
- }
- }
- impl fmt::Display for ScriptStatus {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- ScriptStatus::Unseen => write!(f, "unseen"),
- ScriptStatus::InMempool => write!(f, "in mempool"),
- ScriptStatus::Confirmed(inner) => {
- write!(f, "confirmed with {} blocks", inner.confirmations())
- }
- }
- }
- }
- impl Encodable for bitcoin::Transaction {
- fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
- let tx = self.serialize();
- let len = tx.encode(s)?;
- Ok(len)
- }
- }
- impl Encodable for bitcoin::Address {
- fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
- let addr = self.to_string();
- let len = addr.encode(s)?;
- Ok(len)
- }
- }
- impl Decodable for bitcoin::Address {
- fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
- let addr: String = Decodable::decode(&mut d)?;
- let addr = bitcoin::Address::from_str(&addr)
- .map_err(|err| crate::Error::from(BtcFailed::from(err)))?;
- Ok(addr)
- }
- }
- impl Encodable for bitcoin::PublicKey {
- fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
- let key = self.to_bytes();
- let len = key.encode(s)?;
- Ok(len)
- }
- }
- impl Decodable for bitcoin::PublicKey {
- fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
- let key: Vec<u8> = Decodable::decode(&mut d)?;
- let key = bitcoin::PublicKey::from_slice(&key)
- .map_err(|err| crate::Error::from(BtcFailed::from(err)))?;
- Ok(key)
- }
- }
- impl Encodable for bitcoin::PrivateKey {
- fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
- let key: String = self.to_string();
- let len = key.encode(s)?;
- Ok(len)
- }
- }
- impl Decodable for bitcoin::PrivateKey {
- fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
- let key: String = Decodable::decode(&mut d)?;
- let key = bitcoin::PrivateKey::from_str(&key)
- .map_err(|err| crate::Error::from(BtcFailed::from(err)))?;
- Ok(key)
- }
- }
- impl Encodable for secp256k1::key::PublicKey {
- fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
- let key: Vec<u8> = self.serialize().to_vec();
- let len = key.encode(s)?;
- Ok(len)
- }
- }
- impl Decodable for secp256k1::key::PublicKey {
- fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
- let key: Vec<u8> = Decodable::decode(&mut d)?;
- let key = secp256k1::key::PublicKey::from_slice(&key)
- .map_err(|err| crate::Error::from(BtcFailed::from(err)))?;
- Ok(key)
- }
- }
- // TODO: add secret + public keys together for Encodable
- impl Encodable for Keypair {
- fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
- let key: Vec<u8> = self.to_bytes().to_vec();
- let len = key.encode(s)?;
- Ok(len)
- }
- }
- impl Decodable for Keypair {
- fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
- let key: Vec<u8> = Decodable::decode(&mut d)?;
- let key = Keypair::from_bytes(key.as_slice()).map_err(|_| {
- crate::Error::from(BtcFailed::DecodeAndEncodeError("load keypair from slice".into()))
- })?;
- Ok(key)
- }
- }
- #[derive(Debug, Clone, thiserror::Error)]
- pub enum BtcFailed {
- #[error("There is no enough value {0}")]
- NotEnoughValue(u64),
- #[error("could not parse BTC address: {0}")]
- BadBtcAddress(String),
- #[error("Unable to create Electrum Client: {0}")]
- ElectrumError(String),
- #[error("BtcFailed: {0}")]
- BtcError(String),
- #[error("Decode and decode keys error: {0}")]
- DecodeAndEncodeError(String),
- #[error("Keypair error from Secp256k1: {0}")]
- KeypairError(String),
- #[error("Received Notification Error: {0}")]
- Notification(String),
- }
- impl From<crate::error::Error> for BtcFailed {
- fn from(err: crate::error::Error) -> BtcFailed {
- BtcFailed::BtcError(err.to_string())
- }
- }
- impl From<secp256k1::Error> for BtcFailed {
- fn from(err: secp256k1::Error) -> BtcFailed {
- BtcFailed::KeypairError(err.to_string())
- }
- }
- impl From<bitcoin::util::address::Error> for BtcFailed {
- fn from(err: bitcoin::util::address::Error) -> BtcFailed {
- BtcFailed::BadBtcAddress(err.to_string())
- }
- }
- impl From<bdk::electrum_client::Error> for BtcFailed {
- fn from(err: bdk::electrum_client::Error) -> BtcFailed {
- BtcFailed::ElectrumError(err.to_string())
- }
- }
- impl From<bitcoin::util::key::Error> for BtcFailed {
- fn from(err: bitcoin::util::key::Error) -> BtcFailed {
- BtcFailed::DecodeAndEncodeError(err.to_string())
- }
- }
- impl From<anyhow::Error> for BtcFailed {
- fn from(err: anyhow::Error) -> BtcFailed {
- BtcFailed::DecodeAndEncodeError(err.to_string())
- }
- }
- pub type BtcResult<T> = std::result::Result<T, BtcFailed>;
- #[cfg(test)]
- mod tests {
- use super::Keypair;
- use crate::serial::{deserialize, serialize};
- use secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE};
- use std::str::FromStr;
- const KEYPAIR_LENGTH: usize = SECRET_KEY_SIZE + PUBLIC_KEY_SIZE;
- #[test]
- pub fn test_serialize_btc_address() -> super::BtcResult<()> {
- let btc_addr =
- bitcoin::Address::from_str(&String::from("mxVFsFW5N4mu1HPkxPttorvocvzeZ7KZyk"))?;
- let btc_ser = serialize(&btc_addr);
- let btc_dser = deserialize(&btc_ser)?;
- assert_eq!(btc_addr, btc_dser);
- Ok(())
- }
- #[test]
- pub fn test_serialize_and_deserialize_keypair() -> super::BtcResult<()> {
- let keypair = Keypair::new();
- let bytes: [u8; KEYPAIR_LENGTH] = keypair.to_bytes();
- let keys = Keypair::from_bytes(&bytes)?;
- assert_eq!(keypair, keys);
- Ok(())
- }
- }
|