From 4da355ab0b552e7c989ed05b6b784a2fbd29a786 Mon Sep 17 00:00:00 2001 From: Richard Ulrich Date: Mon, 9 Oct 2023 16:14:36 +0200 Subject: [PATCH 1/5] Adding an azure pipeline --- azure-pipelines.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..7272767 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,25 @@ +trigger: + branches: + include: + - '*' + exclude: + - '*release*' +# - '*feature*' + +pool: + name: "SEBA Azure Self-hosted" + +steps: +- script: make live_create + displayName: 'Build live ISO' +- task: Docker@2 + displayName: 'Build and Push Docker Image' + inputs: + command: 'buildAndPush' + Dockerfile: 'Dockerfile' + buildContext: '' + addBaseImageData: false + containerRegistry: docker.tech.seba.cloud + repository: $(repositoryBase)/$(applicationName) + tags: $(version) + From d91cf8fa22084045da4d3126bd49a0c2d22c3f13 Mon Sep 17 00:00:00 2001 From: Richard Ulrich Date: Thu, 23 Nov 2023 15:41:39 +0100 Subject: [PATCH 2/5] allowing custom repo and MITM CA in the docker container for building --- Cargo.lock | 2 +- Dockerfile | 19 ++++++++++++++++++- Makefile | 4 +++- azure-pipelines.yml | 25 ------------------------- src/main.rs | 1 - 5 files changed, 22 insertions(+), 29 deletions(-) delete mode 100644 azure-pipelines.yml diff --git a/Cargo.lock b/Cargo.lock index ef309bf..835c437 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -321,7 +321,7 @@ dependencies = [ [[package]] name = "bdk-reserves-web" -version = "0.1.8" +version = "0.1.9" dependencies = [ "actix-web", "base64 0.13.1", diff --git a/Dockerfile b/Dockerfile index 028f099..ae5a8f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,25 @@ FROM rust:1.72-alpine3.18 as builder + +ARG ALPINE_REPO +ARG MITM_CA + +# allowing custom package repositories +RUN printf "${ALPINE_REPO}/main\n${ALPINE_REPO}/community\n" > /etc/apk/repositories +RUN cat /etc/apk/repositories + +# allowing MITM attacks (requirement for some build systems) +RUN echo "$MITM_CA" > /root/mitm-ca.crt +RUN cat /root/mitm-ca.crt >> /etc/ssl/certs/ca-certificates.crt +RUN apk --no-cache add ca-certificates \ + && rm -rf /var/cache/apk/* +RUN echo "$MITM_CA" > /usr/local/share/ca-certificates/mitm-ca.crt +RUN update-ca-certificates + RUN apk add --no-cache build-base -USER bin WORKDIR /app COPY . . +RUN mkdir target && chown bin target && mkdir dist && chown bin dist +USER bin RUN cargo test RUN cargo build --release RUN install -D target/release/bdk-reserves-web dist/bin/bdk-reserves-web diff --git a/Makefile b/Makefile index b9be5e5..ac7dc1a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ TAG := bdk-reserves-web +ALPINE_REPO := http://dl-cdn.alpinelinux.org/alpine/v3.18 +MITM_CA := "" run: builder docker run --rm --tty --env PORT=8888 --publish 8888:8888 ${TAG} builder: - docker build --tag ${TAG} . + docker build --tag ${TAG} --build-arg "ALPINE_REPO=${ALPINE_REPO}" --build-arg "MITM_CA=${MITM_CA}" . diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 7272767..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,25 +0,0 @@ -trigger: - branches: - include: - - '*' - exclude: - - '*release*' -# - '*feature*' - -pool: - name: "SEBA Azure Self-hosted" - -steps: -- script: make live_create - displayName: 'Build live ISO' -- task: Docker@2 - displayName: 'Build and Push Docker Image' - inputs: - command: 'buildAndPush' - Dockerfile: 'Dockerfile' - buildContext: '' - addBaseImageData: false - containerRegistry: docker.tech.seba.cloud - repository: $(repositoryBase)/$(applicationName) - tags: $(version) - diff --git a/src/main.rs b/src/main.rs index d9e5e46..3969110 100644 --- a/src/main.rs +++ b/src/main.rs @@ -164,7 +164,6 @@ mod tests { let response_body = resp.into_body(); let resp = r#"{"error":"NonSpendableInput(1)"}"#; assert_eq!(to_bytes(response_body).await?, resp); - //assert_eq!(to_bytes(response_body).await?, r##"Hello world!"##); Ok(()) } From 7c9934a6170f535dac7a528ba0b2dbdd1b3b062d Mon Sep 17 00:00:00 2001 From: Richard Ulrich Date: Tue, 19 Dec 2023 17:18:31 +0100 Subject: [PATCH 3/5] using alpine instead of scratch --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index ae5a8f7..86e203c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN cargo fmt -- --check RUN cargo clippy RUN cargo audit -FROM scratch +FROM alpine COPY --from=builder /app/dist / USER 65534 -ENTRYPOINT ["/bin/bdk-reserves-web"] \ No newline at end of file +ENTRYPOINT ["/bin/bdk-reserves-web"] From 8b6cb866581b3796c0bb0d5c50c0979909da0f5e Mon Sep 17 00:00:00 2001 From: Richard Ulrich Date: Wed, 20 Dec 2023 09:35:07 +0100 Subject: [PATCH 4/5] http_proxy --- Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dockerfile b/Dockerfile index 86e203c..2011552 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,11 @@ FROM rust:1.72-alpine3.18 as builder ARG ALPINE_REPO ARG MITM_CA +ARG http_proxy +ENV http_proxy=$http_proxy +ENV https_proxy=$http_proxy +ENV HTTP_PROXY=$http_proxy +ENV HTTPS_PROXY=$http_proxy # allowing custom package repositories RUN printf "${ALPINE_REPO}/main\n${ALPINE_REPO}/community\n" > /etc/apk/repositories From 7359ec1cf892ebab57e2d6211688d45b762775af Mon Sep 17 00:00:00 2001 From: Richard Ulrich Date: Mon, 5 Feb 2024 14:18:24 +0100 Subject: [PATCH 5/5] adding prometheus reporting for some counters --- Cargo.lock | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/main.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 98 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd17f81..3644171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -328,7 +328,9 @@ dependencies = [ "base64 0.13.1", "bdk", "bdk-reserves", + "lazy_static", "log", + "prometheus", "serde", "serde_json", ] @@ -691,9 +693,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -796,6 +798,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.151" @@ -1001,6 +1009,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.12.1", + "protobuf", + "thiserror", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + [[package]] name = "quote" version = "1.0.33" @@ -1330,6 +1359,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.40", +] + [[package]] name = "time" version = "0.3.30" diff --git a/Cargo.toml b/Cargo.toml index 7aa49ee..38e8810 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ bdk-reserves = "0.28" log = "0.4" base64 = "0.13" prometheus = "0.13" +lazy_static = "1.4" [dev-dependencies] #actix-rt = "2" diff --git a/src/main.rs b/src/main.rs index 3969110..cb6f197 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ use bdk::bitcoin::util::psbt::PartiallySignedTransaction; use bdk::bitcoin::{Address, Network, OutPoint, TxOut}; use bdk::electrum_client::{Client, ElectrumApi}; use bdk_reserves::reserves::verify_proof; +use lazy_static::lazy_static; +use prometheus::{self, register_int_counter, Encoder, IntCounter, TextEncoder}; use serde::{Deserialize, Serialize}; use serde_json::json; use std::{env, io, str::FromStr}; @@ -15,6 +17,16 @@ struct ProofOfReserves { proof_psbt: String, } +lazy_static! { + static ref POR_SUCCESS_COUNTER: IntCounter = + register_int_counter!("POR_success", "Successfully validated proof of reserves").unwrap(); +} + +lazy_static! { + static ref POR_INVALID_COUNTER: IntCounter = + register_int_counter!("POR_invalid", "Invalid proof of reserves").unwrap(); +} + #[actix_web::main] async fn main() -> io::Result<()> { let address = env::var("BIND_ADDRESS").unwrap_or_else(|_err| match env::var("PORT") { @@ -25,12 +37,15 @@ async fn main() -> io::Result<()> { println!("Starting HTTP server at http://{}.", address); println!("You can choose a different address through the BIND_ADDRESS env var."); println!("You can choose a different port through the PORT env var."); + POR_INVALID_COUNTER.reset(); + POR_SUCCESS_COUNTER.reset(); HttpServer::new(|| { App::new() .wrap(middleware::Logger::default()) // <- enable logger .app_data(web::JsonConfig::default().limit(40960)) // <- limit size of the payload (global configuration) .service(web::resource("/proof").route(web::post().to(check_proof))) + .service(web::resource("/prometheus").route(web::get().to(prometheus))) .service(index) }) .bind(address)? @@ -44,6 +59,17 @@ async fn index() -> impl Responder { HttpResponse::Ok().content_type("text/html").body(html) } +async fn prometheus() -> HttpResponse { + let metric_families = prometheus::gather(); + let mut buffer = Vec::new(); + let encoder = TextEncoder::new(); + encoder.encode(&metric_families, &mut buffer).unwrap(); + + let output = String::from_utf8(buffer.clone()).unwrap(); + //println!("************************\nprometheus stats:\n{}", output); + HttpResponse::Ok().content_type("text/plain").body(output) +} + async fn check_proof(item: web::Json, req: HttpRequest) -> HttpResponse { println!("request: {:?}", req); println!("model: {:?}", item); @@ -52,8 +78,14 @@ async fn check_proof(item: web::Json, req: HttpRequest) -> Http handle_ext_reserves(&item.message, &item.proof_psbt, 3, item.addresses.clone()); let answer = match proof_result { - Err(e) => json!({ "error": e }), - Ok(res) => res, + Err(e) => { + POR_INVALID_COUNTER.inc(); + json!({ "error": e }) + } + Ok(res) => { + POR_SUCCESS_COUNTER.inc(); + res + } } .to_string(); HttpResponse::Ok().content_type("text/json").body(answer) @@ -147,7 +179,9 @@ mod tests { #[actix_web::test] async fn test_index() -> Result<(), Error> { - let app = App::new().route("/proof", web::post().to(check_proof)); + let app = App::new() + .route("/proof", web::post().to(check_proof)) + .route("/prometheus", web::get().to(prometheus)); let app = test::init_service(app).await; let req = test::TestRequest::post().uri("/proof") @@ -165,6 +199,15 @@ mod tests { let resp = r#"{"error":"NonSpendableInput(1)"}"#; assert_eq!(to_bytes(response_body).await?, resp); + let req = test::TestRequest::get().uri("/prometheus").to_request(); + let resp = app.call(req).await?; + + assert_eq!(resp.status(), http::StatusCode::OK); + + let response_body = resp.into_body(); + let resp = "# HELP POR_invalid Invalid proof of reserves\n# TYPE POR_invalid counter\nPOR_invalid 1\n"; + assert_eq!(to_bytes(response_body).await?, resp); + Ok(()) } }