Skip to content

Commit

Permalink
replace tracing with otel (#20)
Browse files Browse the repository at this point in the history
* fix tests

* replace tracing with otel
  • Loading branch information
lsunsi authored Jul 22, 2024
1 parent 6923e42 commit 2af3dd9
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 52 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ edition = "2021"
[dependencies]
bitcoin = { version= "0.31.0", features = ["std"], default-features = false }
bitcoincore-rpc = { version = "0.18.0", default-features = false }
opentelemetry = { version = "0.23.0", features = ["trace"], default-features = false }
tokio = { version = "1.12.0", features = ["rt", "macros"], default-features = false }
tracing = { version = "0.1.37", default-features = false }
86 changes: 35 additions & 51 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,44 @@ use bitcoincore_rpc::{
json::{AddressType, GetBalancesResult, ListTransactionResult},
RpcApi,
};
use opentelemetry::trace::{FutureExt, TraceContextExt};
use std::{future::Future, sync::Arc};
use tracing::Instrument;

pub use bitcoincore_rpc as rpc;

#[derive(Clone)]
pub struct Btc {
client: Arc<bitcoincore_rpc::Client>,
tracer: Arc<opentelemetry::global::BoxedTracer>,
}

pub fn build(rpc_user: &str, rpc_password: &str, rpc_url: &str) -> bitcoincore_rpc::Result<Btc> {
let auth = bitcoincore_rpc::Auth::UserPass(rpc_user.into(), rpc_password.into());

bitcoincore_rpc::Client::new(rpc_url, auth).map(|client| Btc {
tracer: Arc::new(opentelemetry::global::tracer("btcore")),
client: Arc::new(client),
})
}

impl Btc {
pub fn get_block_count(&self) -> impl Future<Output = bitcoincore_rpc::Result<u64>> {
let span = start_span(self.tracer.as_ref(), "get_block_count");
let client = self.client.clone();

async move {
tokio::task::spawn_blocking(move || client.get_block_count())
.await
.unwrap()
}
.instrument(span!("get_block_count"))
.with_context(opentelemetry::Context::current_with_span(span))
}

pub fn list_transactions(
&self,
count: usize,
) -> impl Future<Output = bitcoincore_rpc::Result<Vec<ListTransactionResult>>> {
let span = start_span(self.tracer.as_ref(), "listtransactions");
let client = self.client.clone();

async move {
Expand All @@ -47,7 +51,7 @@ impl Btc {
.await
.unwrap()
}
.instrument(span!("listtransactions"))
.with_context(opentelemetry::Context::current_with_span(span))
}

pub fn list_since_block(
Expand All @@ -56,6 +60,7 @@ impl Btc {
confirmations: usize,
) -> impl Future<Output = bitcoincore_rpc::Result<(Vec<ListTransactionResult>, BlockHash)>>
{
let span = start_span(self.tracer.as_ref(), "listsinceblock");
let client = self.client.clone();

async move {
Expand All @@ -67,76 +72,80 @@ impl Btc {
.await
.unwrap()
}
.instrument(span!("listsinceblock"))
.with_context(opentelemetry::Context::current_with_span(span))
}

pub fn get_balances(&self) -> impl Future<Output = bitcoincore_rpc::Result<GetBalancesResult>> {
let span = start_span(self.tracer.as_ref(), "getbalances");
let client = self.client.clone();

async move {
tokio::task::spawn_blocking(move || client.get_balances())
.await
.unwrap()
}
.instrument(span!("getbalances"))
.with_context(opentelemetry::Context::current_with_span(span))
}

pub fn get_balance(
&self,
number_of_confirmations: Option<usize>,
) -> impl Future<Output = bitcoincore_rpc::Result<Amount>> {
let span = start_span(self.tracer.as_ref(), "getbalance");
let client = self.client.clone();

async move {
tokio::task::spawn_blocking(move || client.get_balance(number_of_confirmations, None))
.await
.unwrap()
}
.instrument(span!("getbalance"))
.with_context(opentelemetry::Context::current_with_span(span))
}

pub fn get_transaction(
&self,
txid: Txid,
) -> impl Future<Output = bitcoincore_rpc::Result<GetTransactionResult>> {
let span = start_span(self.tracer.as_ref(), "gettransaction");
let client = self.client.clone();

async move {
tokio::task::spawn_blocking(move || client.get_transaction(&txid, Some(true)))
.await
.unwrap()
}
.instrument(span!("gettransaction"))
.with_context(opentelemetry::Context::current_with_span(span))
}

pub fn generate_address(
&self,
address_type: AddressType,
) -> impl Future<Output = bitcoincore_rpc::Result<Address<NetworkUnchecked>>> {
let span = start_span(self.tracer.as_ref(), "getnewaddress");
let client = self.client.clone();

async move {
tokio::task::spawn_blocking(move || client.get_new_address(None, Some(address_type)))
.await
.unwrap()
}
.instrument(span!("getnewaddress"))
.with_context(opentelemetry::Context::current_with_span(span))
}
}

#[macro_export]
macro_rules! span {
($method:literal) => {
tracing::info_span!(
"btcore",
service.name = "btcore",
otel.name = $method,
otel.kind = "client",
rpc.system = "jsonrpc",
rpc.service = "bitcoind",
rpc.method = $method,
)
};
fn start_span(
tracer: &impl opentelemetry::trace::Tracer,
method: &'static str,
) -> impl opentelemetry::trace::Span {
opentelemetry::trace::SpanBuilder::from_name(method)
.with_kind(opentelemetry::trace::SpanKind::Client)
.with_attributes([
opentelemetry::KeyValue::new("service.name", "btcore"),
opentelemetry::KeyValue::new("rpc.service", "bitcoind"),
opentelemetry::KeyValue::new("rpc.system", "jsonrpc"),
opentelemetry::KeyValue::new("rpc.method", method),
])
.start(tracer)
}

#[cfg(test)]
Expand All @@ -153,6 +162,7 @@ mod test {

let auth = bitcoincore_rpc::Auth::UserPass(USERNAME.into(), PASSWORD.into());
let client = bitcoincore_rpc::Client::new(RPC_URL, auth).map(|client| Btc {
tracer: Arc::new(opentelemetry::global::tracer("btcore")),
client: Arc::new(client),
})?;

Expand Down Expand Up @@ -183,6 +193,7 @@ mod test {
tokio::task::spawn_blocking(move || client.unload_wallet(Some(&wallet_name)))
.await
.unwrap()
.map(|_| ())
}

fn delete_wallet(&self, wallet_name: &str) -> Result<(), std::io::Error> {
Expand Down Expand Up @@ -252,10 +263,11 @@ mod test {
let address = if burn_coins {
Address::from_str("bcrt1qs758ursh4q9z627kt3pp5yysm78ddny6txaqgw").unwrap()
} else {
self.generate_address_async(AddressType::P2shSegwit)
self.generate_address(AddressType::P2shSegwit)
.await
.unwrap()
};
}
.assume_checked();

tokio::task::spawn_blocking(move || client.generate_to_address(block_num, &address))
.await
Expand Down Expand Up @@ -347,32 +359,4 @@ mod test {

assert_eq!(balances.mine.trusted, Amount::ZERO);
}

#[tokio::test]
async fn test_get_transaction_fee() {
let client = build_for_test().await.unwrap();

client.generate_one_spendable_output().await.unwrap();

let fee_rate = 1 as i64;

let txid = client
.send_to_address(
client
.generate_address_async(AddressType::P2shSegwit)
.await
.unwrap(),
5_000_000,
Some(fee_rate as i32),
)
.await
.unwrap();

client.generate_n_blocks(1, true).await.unwrap();

let fee = client.get_transaction_fee(txid).await.unwrap().unwrap();
let expected_fee = -(fee.vsize as i64 * fee_rate);

assert_eq!(fee.fee, expected_fee);
}
}

0 comments on commit 2af3dd9

Please sign in to comment.