Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add bria-client to stablesats #411

Merged
merged 43 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
0f78ef5
chore: add bria_serivce proto
thevaibhav-dixit Aug 25, 2023
a8307fa
feat: add bria-client
thevaibhav-dixit Aug 25, 2023
09f68e1
chore: add bria-config in cli config
thevaibhav-dixit Aug 25, 2023
5567aa4
chore: add bria-client to workspace members
thevaibhav-dixit Aug 25, 2023
f089fd6
refactor: refactor bria_client
thevaibhav-dixit Aug 25, 2023
e228bac
chore: add errors for bria_client
thevaibhav-dixit Aug 25, 2023
168901d
test: fix test after refactor
thevaibhav-dixit Aug 25, 2023
2fc0e3c
chore: use bria_client to generate onchain address in hedging module
thevaibhav-dixit Aug 25, 2023
23e5bc7
chore: allow too many arguments
thevaibhav-dixit Aug 25, 2023
9ef98eb
chore: use better error message
thevaibhav-dixit Aug 25, 2023
2c5b60d
chore: add comments for bria env variables
thevaibhav-dixit Aug 25, 2023
cb6d6c4
chore: only pass BRIA_KEY as env variable
thevaibhav-dixit Aug 26, 2023
3eeb951
refactor: onchain_address fn can also generate address
thevaibhav-dixit Aug 26, 2023
5a1d25f
feat: add send_onchain_payment for bria
thevaibhav-dixit Aug 28, 2023
be9643d
chore: add serde_json to deps
thevaibhav-dixit Aug 28, 2023
410e984
test: add send_onchain_payment
thevaibhav-dixit Aug 28, 2023
26169f1
test: fix bria_client_config
thevaibhav-dixit Aug 28, 2023
2d66f03
chore: remove galoy client from hedging module
thevaibhav-dixit Aug 28, 2023
54e20eb
chore: fix naming of errors
thevaibhav-dixit Aug 28, 2023
a54c6f0
chore: use proper type for metadata
thevaibhav-dixit Aug 28, 2023
7963fd5
fix: remove to_string() from metadata
thevaibhav-dixit Aug 28, 2023
3690e79
fix: address pr reviews
thevaibhav-dixit Aug 28, 2023
0ff6553
feat: add bria image in docker compose
thevaibhav-dixit Aug 29, 2023
ce719db
test: use dev constants for bria_client_config
thevaibhav-dixit Aug 29, 2023
3638486
chore: add dev and docker-compose to typos.toml
thevaibhav-dixit Aug 29, 2023
28ca36c
chore: add BRIA_URL to integration-tests
thevaibhav-dixit Aug 29, 2023
c3a3d8e
test: pass in BRIA_URL as env var
thevaibhav-dixit Aug 29, 2023
deaee76
fix: prefix http in BRIA_URL
thevaibhav-dixit Aug 29, 2023
aa31bf4
chore: increase wait time for hedging test
thevaibhav-dixit Aug 29, 2023
afefa21
fix: add TonicError for bria errors
thevaibhav-dixit Aug 29, 2023
8c66cb9
fix: clippy warnings
thevaibhav-dixit Aug 29, 2023
16a7f8c
fix: return AddressNotFound when status code is NotFound
thevaibhav-dixit Aug 29, 2023
94c3611
refactor: inline new_address and get_address into onchain_address
thevaibhav-dixit Aug 29, 2023
7974c0a
chore(deps): update deps and ignore RUSTSEC-2023-0052
thevaibhav-dixit Aug 29, 2023
3e436a3
chore: remove deprecated from_utc
thevaibhav-dixit Aug 29, 2023
c5cbb15
chore: remove redundant errors
thevaibhav-dixit Aug 31, 2023
24398af
chore: introduce flake
bodymindarts Aug 31, 2023
21d97b2
chore: remove redundant galoy-client code
bodymindarts Aug 31, 2023
8c5fa08
chore: add bria-client instrumentation
bodymindarts Aug 31, 2023
d469313
refactor: simpler bria-client structure
bodymindarts Aug 31, 2023
7a7d0da
chore: remove more redundant galoy-client code
bodymindarts Aug 31, 2023
a60f7fd
fix: do not eat bria errors
bodymindarts Aug 31, 2023
0c71d60
docs: update stablesats.yml
bodymindarts Aug 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ members = [
"bitfinex-client",
"bitfinex-price",
"galoy-client",
"bria-client",
]
44 changes: 44 additions & 0 deletions bria-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[package]
name = "bria-client"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]

fail-on-warnings = []

[dependencies]
shared = { path = "../shared", package = "stablesats-shared" }

anyhow = "1.0.72"
chrono = { version = "0.4", features = [
"clock",
"serde",
], default-features = false }
prost = "0.11"
tonic = "0.9"
axum-core = "0.3.4"
tokio = "1.29.1"
futures = "0.3.27"
thiserror = "1.0.40"
serde = { version = "1.0.158", features = ["derive"] }
rust_decimal = "1.29.0"
tracing = "0.1.37"
opentelemetry = { version = "0.18.0", features = ["trace"] }
tracing-opentelemetry = "0.18.0"
rust_decimal_macros = "1.29.0"
rusty-money = "0.4.1"
serde_json = "1.0.93"
serde_with = { version = "2.3.1", features = ["chrono_0_4"] }
url = "2.4.0"
async-trait = "0.1.67"
prost-wkt-types = { version = "0.4.2", features = ["vendored-protoc"]}

[build-dependencies]
protobuf-src = { version = "1.1.0" }
tonic-build = { version = "0.8", features = ["prost"] }

[dev-dependencies]
anyhow = "1.0.70"
11 changes: 11 additions & 0 deletions bria-client/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("cargo:rerun-if-changed=migrations");
std::env::set_var("PROTOC", protobuf_src::protoc());

tonic_build::configure()
.type_attribute(".", "#[derive(serde::Serialize)]")
.type_attribute(".", "#[serde(rename_all = \"camelCase\")]")
.extern_path(".google.protobuf.Struct", "::prost_wkt_types::Struct")
.compile(&["../proto/bria/bria_service.proto"], &["../proto"])?;
Ok(())
}
113 changes: 113 additions & 0 deletions bria-client/src/client/bria_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use super::{config::BriaClientConfig, proto};
use crate::error::BriaClientError;

type ProtoClient = proto::bria_service_client::BriaServiceClient<tonic::transport::Channel>;

pub const PROFILE_API_KEY_HEADER: &str = "x-bria-api-key";

#[derive(Debug)]
pub struct OnchainAddress {
pub address: String,
}

#[derive(Debug, Clone)]
pub struct BriaClient {
config: BriaClientConfig,
proto_client: ProtoClient,
}

impl BriaClient {
pub async fn connect(config: BriaClientConfig) -> Result<Self, BriaClientError> {
let proto_client = ProtoClient::connect(config.url.clone())
.await
.map_err(|_| BriaClientError::ConnectionError(config.url.clone()))?;

if config.key.is_empty() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too late to validate. It should be asserted in cli.

return Err(BriaClientError::EmptyKey);
}

Ok(Self {
config,
proto_client,
})
}

pub fn inject_auth_token<T>(
&self,
mut request: tonic::Request<T>,
) -> Result<tonic::Request<T>, BriaClientError> {
let key = &self.config.key;
request.metadata_mut().insert(
PROFILE_API_KEY_HEADER,
tonic::metadata::MetadataValue::try_from(key)
.map_err(|_| BriaClientError::CouldNotCreateMetadataValue)?,
);
Ok(request)
}

pub async fn onchain_address(&mut self) -> Result<OnchainAddress, BriaClientError> {
match self.get_address().await {
Ok(addr) => Ok(addr),
Err(_) => self.new_address().await,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't eat this error - only do new_address if the error is NotFound.

}
}

async fn get_address(&mut self) -> Result<OnchainAddress, BriaClientError> {
let request = tonic::Request::new(proto::GetAddressRequest {
identifier: Some(proto::get_address_request::Identifier::ExternalId(
self.config.external_id.clone(),
)),
});

self.proto_client
.get_address(self.inject_auth_token(request)?)
.await
.ok()
.and_then(|res| {
res.into_inner()
.address
.map(|addr| OnchainAddress { address: addr })
})
.ok_or(BriaClientError::AddressNotFound)
}

async fn new_address(&mut self) -> Result<OnchainAddress, BriaClientError> {
let request = tonic::Request::new(proto::NewAddressRequest {
wallet_name: self.config.wallet_name.clone(),
external_id: Some(self.config.external_id.clone()),
metadata: None,
});
self.proto_client
.new_address(self.inject_auth_token(request)?)
.await
.map(|res| OnchainAddress {
address: res.into_inner().address,
})
.map_err(|e| BriaClientError::CouldNotGenerateNewAddress(e.message().to_string()))
}

pub async fn send_onchain_payment(
&mut self,
destination: String,
satoshis: u64,
metadata: Option<serde_json::Value>,
) -> Result<String, BriaClientError> {
let request = tonic::Request::new(proto::SubmitPayoutRequest {
wallet_name: self.config.wallet_name.clone(),
payout_queue_name: self.config.payout_queue_name.clone(),
destination: Some(proto::submit_payout_request::Destination::OnchainAddress(
destination,
)),
satoshis,
external_id: None,
metadata: metadata.map(serde_json::from_value).transpose()?,
});

let response = self
.proto_client
.submit_payout(self.inject_auth_token(request)?)
.await
.map_err(|e| BriaClientError::CouldNotSendOnchainPayment(e.message().to_string()))?;
Ok(response.into_inner().id)
}
}
19 changes: 19 additions & 0 deletions bria-client/src/client/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct BriaClientConfig {
#[serde(default = "default_url")]
pub url: String,
#[serde(default)]
pub key: String,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we be more specific here? profile_api_key perhaps?

#[serde(default)]
pub wallet_name: String,
#[serde(default)]
pub payout_queue_name: String,
#[serde(default)]
pub external_id: String,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about onchain_address_external_id?

}

fn default_url() -> String {
"http://localhost:2742".to_string()
}
10 changes: 10 additions & 0 deletions bria-client/src/client/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#[allow(clippy::all)]
pub mod proto {
tonic::include_proto!("services.bria.v1");
}

mod bria_client;
mod config;

pub use bria_client::*;
pub use config::*;
25 changes: 25 additions & 0 deletions bria-client/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use thiserror::Error;

#[derive(Error, Debug)]
pub enum BriaClientError {
#[error("Couldn't connect to bria at url: {0}")]
ConnectionError(String),
#[error("Bria key cannot be empty")]
EmptyKey,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should need this error.

#[error("Couldn't create MetadataValue")]
CouldNotCreateMetadataValue,
#[error("Couldn't find address for the given external_id")]
AddressNotFound,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is this returned? This should be handled internally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when we call onchain_address(), it first calls get_address(). suppose we don't have an address for a given onchain_address_external_id we use Err(BriaClientError::AddressNotFound) => self.new_address().await to call new_address()

#[error("Couldn't generate a new address: {0}")]
CouldNotGenerateNewAddress(String),
#[error("Couldn't send onchain payment: {0}")]
CouldNotSendOnchainPayment(String),
#[error("Could not parse Send Onchain Payment Metadata: {0}")]
CouldNotParseSendOnchainPaymentMetadata(serde_json::Error),
}

impl From<serde_json::Error> for BriaClientError {
fn from(err: serde_json::Error) -> BriaClientError {
BriaClientError::CouldNotParseSendOnchainPaymentMetadata(err)
}
}
Loading