1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
use anyhow::{Context, Result};
use bdk::{
bitcoin::Network,
blockchain::{ElectrumBlockchain, GetHeight},
electrum_client::{Client, ConfigBuilder, Socks5Config},
};
use serde::Deserialize;
fn get_default_electrum_server(network: Network) -> &'static str {
match network {
Network::Bitcoin => "ssl://fulcrum.sethforprivacy.com:50002",
Network::Testnet => "ssl://electrum.blockstream.info:60002",
Network::Signet => "ssl://mempool.space:60602",
_ => panic!("unsupported network"),
}
}
#[derive(Deserialize, Default, Debug)]
pub struct ElectrumConfig {
url: Option<String>,
network: Option<Network>,
socks5: Option<String>,
#[serde(default)]
certificate_validation: bool,
}
impl ElectrumConfig {
pub fn url(&self) -> &str {
self.url
.as_deref()
.unwrap_or(get_default_electrum_server(self.network()))
}
pub fn network(&self) -> Network {
self.network.unwrap_or(Network::Bitcoin)
}
pub fn certificate_validation(&self) -> bool {
self.certificate_validation
}
pub fn socks5(&self) -> Option<Socks5Config> {
self.socks5.as_ref().map(Socks5Config::new)
}
}
pub fn get_blockchain(electrum_cfg: &ElectrumConfig) -> Result<ElectrumBlockchain> {
let server_cfg = ConfigBuilder::new()
.validate_domain(electrum_cfg.certificate_validation())
.socks5(electrum_cfg.socks5())
.build();
let electrum_url = electrum_cfg.url();
let client = Client::from_config(electrum_url, server_cfg)
.with_context(|| "could not configure electrum client".to_string())?;
Ok(ElectrumBlockchain::from(client))
}
pub struct BlockchainState {
height: Option<u32>,
url: String,
blockchain: ElectrumBlockchain,
}
impl BlockchainState {
pub fn new(electrum_cfg: &ElectrumConfig) -> Result<Self> {
Ok(Self {
height: Default::default(),
url: String::from(electrum_cfg.url()),
blockchain: get_blockchain(electrum_cfg)?,
})
}
pub fn update_height(&mut self) {
match self.blockchain.get_height() {
Ok(polled_height) => {
match self.height {
Some(h) => {
if polled_height != h {
self.height = Some(polled_height);
debug!("current block height: {}", polled_height);
}
}
None => {
self.height = Some(polled_height);
info!("connected to '{}'", self.url);
info!("current block height: {}", polled_height);
}
};
}
Err(e) => warn!("could not reach '{}': {}", self.url, e),
};
}
}
|