Skip to content

Commit

Permalink
compliance: compliance-server: Define initial compliance api
Browse files Browse the repository at this point in the history
  • Loading branch information
joeykraut committed Jun 29, 2024
1 parent ec8fcf2 commit 9e5d729
Show file tree
Hide file tree
Showing 22 changed files with 176 additions and 1,269 deletions.
15 changes: 15 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[workspace]
members = [
"compliance/compliance-server",
"compliance/compliance-api",
"dealer/renegade-dealer",
"dealer/renegade-dealer-api",
"fee-sweeper",
Expand All @@ -14,3 +16,16 @@ debug = true
[profile.release]
opt-level = 3 # Full optimizations
lto = true

[workspace.dependencies]
# === Renegade Dependencies === #
arbitrum-client = { git = "https://github.com/renegade-fi/renegade.git", features = [
"rand",
] }
renegade-api = { package = "external-api", git = "https://github.com/renegade-fi/renegade.git" }
renegade-common = { package = "common", git = "https://github.com/renegade-fi/renegade.git" }
renegade-constants = { package = "constants", git = "https://github.com/renegade-fi/renegade.git" }
renegade-circuits = { package = "circuits", git = "https://github.com/renegade-fi/renegade.git" }
renegade-circuit-types = { package = "circuit-types", git = "https://github.com/renegade-fi/renegade.git" }
renegade-crypto = { git = "https://github.com/renegade-fi/renegade.git" }
renegade-util = { package = "util", git = "https://github.com/renegade-fi/renegade.git" }
8 changes: 8 additions & 0 deletions compliance/compliance-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "compliance-api"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1" }
17 changes: 17 additions & 0 deletions compliance/compliance-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use serde::{Deserialize, Serialize};

/// The response type for a compliance check
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceCheckResponse {
/// The compliance status of the wallet
pub compliance_status: ComplianceStatus,
}

/// The status on compliance for a wallet
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ComplianceStatus {
/// The wallet is compliant
Compliant,
/// The wallet is not compliant
NotCompliant,
}

Check failure on line 17 in compliance/compliance-api/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for the crate

error: missing documentation for the crate --> compliance/compliance-api/src/lib.rs:1:1 | 1 | / use serde::{Deserialize, Serialize}; 2 | | 3 | | /// The response type for a compliance check 4 | | #[derive(Debug, Clone, Serialize, Deserialize)] ... | 16 | | NotCompliant, 17 | | } | |_^ | = note: requested on the command line with `-D missing-docs`

Check failure on line 17 in compliance/compliance-api/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

missing documentation for the crate

error: missing documentation for the crate --> compliance/compliance-api/src/lib.rs:1:1 | 1 | / use serde::{Deserialize, Serialize}; 2 | | 3 | | /// The response type for a compliance check 4 | | #[derive(Debug, Clone, Serialize, Deserialize)] ... | 16 | | NotCompliant, 17 | | } | |_^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items = note: requested on the command line with `-D clippy::missing-docs-in-private-items`
17 changes: 17 additions & 0 deletions compliance/compliance-server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "compliance-server"
version = "0.1.0"
edition = "2021"

[dependencies]
# === HTTP Server === #
http-body-util = "0.1.0"
warp = "0.3"
compliance-api = { path = "../compliance-api" }

# === Renegade Dependencies === #
renegade-util = { workspace = true }

# === Misc === #
clap = { version = "4.5", features = ["derive"] }
tokio = { version = "1.37", features = ["full"] }
17 changes: 17 additions & 0 deletions compliance/compliance-server/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! Error types for the compliance server

use std::{error::Error, fmt::Display};

use warp::reject::Reject;

/// The error type emitted by the compliance server
#[derive(Debug, Clone)]
pub enum ComplianceServerError {}

impl Display for ComplianceServerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ComplianceServerError")
}
}
impl Error for ComplianceServerError {}
impl Reject for ComplianceServerError {}
68 changes: 68 additions & 0 deletions compliance/compliance-server/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use clap::Parser;
use compliance_api::{ComplianceCheckResponse, ComplianceStatus};
use error::ComplianceServerError;
use renegade_util::telemetry::{setup_system_logger, LevelFilter};
use warp::{reply::Json, Filter};

pub mod error;

/// The CLI for the compliance server
#[derive(Debug, Clone, Parser)]
#[command(about = "The CLI for the compliance server")]
struct Cli {
/// The port to listen on
#[arg(short, long)]
port: u16,
/// The Chainalysis API key
#[arg(long)]
chainalysis_api_key: String,
}

#[tokio::main]
async fn main() {
setup_system_logger(LevelFilter::INFO);
let cli = Cli::parse();

// Get compliance information for a wallet
let chainalysis_key = cli.chainalysis_api_key.clone();
let compliance_check = warp::get()
.and(warp::path("v0"))
.and(warp::path("compliance-check"))
.and(warp::path::param::<String>()) // wallet_address
.and_then(move |wallet_address| {
let key = chainalysis_key.clone();
async move {
handle_req(wallet_address, &key).await
}
});

// GET /ping
let ping = warp::get()
.and(warp::path("ping"))
.map(|| warp::reply::with_status("PONG", warp::http::StatusCode::OK));

let routes = compliance_check.or(ping);
warp::serve(routes).run(([0, 0, 0, 0], cli.port)).await
}

/// Handle a request for a compliance check
async fn handle_req(
wallet_address: String,
chainalysis_api_key: &str,
) -> Result<Json, warp::Rejection> {
let compliance_status = check_wallet_compliance(wallet_address, chainalysis_api_key).await?;
let resp = ComplianceCheckResponse { compliance_status };
Ok(warp::reply::json(&resp))
}

/// Check the compliance of a wallet
async fn check_wallet_compliance(
wallet_address: String,
chainalysis_api_key: &str,
) -> Result<ComplianceStatus, ComplianceServerError> {
// 1. Check the DB first

// 2. If not present, check the chainalysis API

todo!()
}
18 changes: 8 additions & 10 deletions fee-sweeper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ alloy-sol-types = "0.3.1"
ethers = "2"

# === Renegade Dependencies === #
arbitrum-client = { git = "https://github.com/renegade-fi/renegade.git", features = [
"rand",
] }
renegade-api = { package = "external-api", git = "https://github.com/renegade-fi/renegade.git" }
renegade-common = { package = "common", git = "https://github.com/renegade-fi/renegade.git" }
renegade-constants = { package = "constants", git = "https://github.com/renegade-fi/renegade.git" }
renegade-circuits = { package = "circuits", git = "https://github.com/renegade-fi/renegade.git" }
renegade-circuit-types = { package = "circuit-types", git = "https://github.com/renegade-fi/renegade.git" }
renegade-crypto = { git = "https://github.com/renegade-fi/renegade.git" }
renegade-util = { package = "util", git = "https://github.com/renegade-fi/renegade.git" }
arbitrum-client = { workspace = true, features = ["rand"] }
renegade-api = { package = "external-api", workspace = true }
renegade-common = { package = "common", workspace = true }
renegade-constants = { package = "constants", workspace = true }
renegade-circuits = { package = "circuits", workspace = true }
renegade-circuit-types = { package = "circuit-types", workspace = true }
renegade-crypto = { workspace = true }
renegade-util = { package = "util", workspace = true }

# === Misc Dependencies === #
base64 = "0.22"
Expand Down
14 changes: 2 additions & 12 deletions fee-sweeper/src/db/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,7 @@ impl NewFee {
let blinder = scalar_to_bigint(&note.blinder).into();
let receiver = jubjub_to_hex_string(&note.receiver);

NewFee {
tx_hash,
mint,
amount,
blinder,
receiver,
}
NewFee { tx_hash, mint, amount, blinder, receiver }
}
}

Expand Down Expand Up @@ -79,10 +73,6 @@ pub struct WalletMetadata {
impl WalletMetadata {
/// Construct a new wallet metadata entry
pub fn empty(id: Uuid, secret_id: String) -> Self {
WalletMetadata {
id,
mints: vec![],
secret_id,
}
WalletMetadata { id, mints: vec![], secret_id }
}
}
6 changes: 1 addition & 5 deletions fee-sweeper/src/db/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,4 @@ diesel::table! {
}
}

diesel::allow_tables_to_appear_in_same_query!(
fees,
indexing_metadata,
wallets,
);
diesel::allow_tables_to_appear_in_same_query!(fees, indexing_metadata, wallets,);
5 changes: 3 additions & 2 deletions fee-sweeper/src/indexer/index_fees.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Phase one of the sweeper's execution; index all fees since the last consistent block
//! Phase one of the sweeper's execution; index all fees since the last
//! consistent block

use alloy_sol_types::SolCall;
use arbitrum_client::abi::settleOfflineFeeCall;
Expand Down Expand Up @@ -99,7 +100,7 @@ impl Indexer {
<settleOfflineFeeCall as SolCall>::SELECTOR => {
parse_note_ciphertext_from_settle_offline_fee(&calldata)
.map_err(raw_err_str!("failed to parse ciphertext: {}"))?
}
},
sel => return Err(format!("invalid selector when parsing note: {sel:?}")),
};

Expand Down
19 changes: 6 additions & 13 deletions fee-sweeper/src/indexer/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,7 @@ impl Indexer {
.map(|res: Vec<Metadata>| res[0].clone())
.map_err(raw_err_str!("failed to query latest block: {}"))?;

entry
.value
.parse::<u64>()
.map_err(raw_err_str!("failed to parse latest block: {}"))
entry.value.parse::<u64>().map_err(raw_err_str!("failed to parse latest block: {}"))
}

/// Update the latest block number
Expand Down Expand Up @@ -143,8 +140,8 @@ impl Indexer {
return Ok(vec![]);
}

// We query the fees table with a transformation that calculates the value of each fee using the prices passed in.
// This query looks something like:
// We query the fees table with a transformation that calculates the value of
// each fee using the prices passed in. This query looks something like:
// SELECT tx_hash, mint, amount,
// CASE
// WHEN mint = '<mint1>' then amount * <price1>
Expand All @@ -163,10 +160,8 @@ impl Indexer {
query_string.push_str(&format!("WHEN mint = '{}' then amount * {} ", mint, price));
}
query_string.push_str("ELSE 0 END as value ");
query_string.push_str(&format!(
"FROM fees WHERE redeemed = false and receiver = '{}'",
receiver
));
query_string
.push_str(&format!("FROM fees WHERE redeemed = false and receiver = '{}'", receiver));

// Sort and limit
query_string.push_str(&format!("ORDER BY value DESC LIMIT {};", MAX_FEES_REDEEMED));
Expand Down Expand Up @@ -204,9 +199,7 @@ impl Indexer {
let wallets = wallet_table
.filter(n_mints.lt(MAX_BALANCES as i32))
.load(&mut self.db_conn)
.map_err(raw_err_str!(
"failed to query wallets with empty balances: {}"
))?;
.map_err(raw_err_str!("failed to query wallets with empty balances: {}"))?;

Ok(wallets.first().cloned())
}
Expand Down
44 changes: 12 additions & 32 deletions fee-sweeper/src/relayer_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,7 @@ pub struct RelayerClient {
impl RelayerClient {
/// Create a new relayer client
pub fn new(base_url: &str, usdc_mint: &str) -> Self {
Self {
base_url: base_url.to_string(),
usdc_mint: usdc_mint.to_string(),
}
Self { base_url: base_url.to_string(), usdc_mint: usdc_mint.to_string() }
}

/// Get the price for a given mint
Expand All @@ -77,7 +74,7 @@ impl RelayerClient {
state => {
warn!("Price report state: {state:?}");
Ok(None)
}
},
}
}

Expand All @@ -97,11 +94,7 @@ impl RelayerClient {

let keychain = derive_wallet_keychain(eth_key, chain_id).unwrap();
let root_key = keychain.secret_keys.sk_root.unwrap();
if self
.get_relayer_with_auth::<GetWalletResponse>(&path, &root_key)
.await
.is_ok()
{
if self.get_relayer_with_auth::<GetWalletResponse>(&path, &root_key).await.is_ok() {
return Ok(());
}

Expand Down Expand Up @@ -131,9 +124,7 @@ impl RelayerClient {

/// Create a new wallet via the configured relayer
pub(crate) async fn create_new_wallet(&self, wallet: Wallet) -> Result<(), String> {
let body = CreateWalletRequest {
wallet: wallet.into(),
};
let body = CreateWalletRequest { wallet: wallet.into() };

let resp: CreateWalletResponse = self.post_relayer(CREATE_WALLET_ROUTE, &body).await?;
self.await_relayer_task(resp.task_id).await
Expand Down Expand Up @@ -163,8 +154,7 @@ impl RelayerClient {
Req: Serialize,
Resp: for<'de> Deserialize<'de>,
{
self.post_relayer_with_headers(path, body, &HeaderMap::new())
.await
self.post_relayer_with_headers(path, body, &HeaderMap::new()).await
}

/// Post to the relayer with wallet auth
Expand Down Expand Up @@ -211,9 +201,7 @@ impl RelayerClient {
return Err(format!("Failed to send request: {}", resp.status()));
}

resp.json::<Resp>()
.await
.map_err(raw_err_str!("Failed to parse response: {}"))
resp.json::<Resp>().await.map_err(raw_err_str!("Failed to parse response: {}"))
}

/// Get from the relayer URL
Expand Down Expand Up @@ -260,9 +248,7 @@ impl RelayerClient {
return Err(format!("Failed to get relayer path: {}", resp.status()));
}

resp.json::<Resp>()
.await
.map_err(raw_err_str!("Failed to parse response: {}"))
resp.json::<Resp>().await.map_err(raw_err_str!("Failed to parse response: {}"))
}

/// Await a relayer task
Expand All @@ -273,13 +259,10 @@ impl RelayerClient {
// Enter a polling loop until the task finishes
let poll_interval = Duration::from_millis(POLL_INTERVAL_MS);
loop {
// For now, we assume that an error is a 404 in which case the task has completed
// TODO: Improve this break condition if it proves problematic
if self
.get_relayer::<GetTaskStatusResponse>(&path)
.await
.is_err()
{
// For now, we assume that an error is a 404 in which case the task has
// completed TODO: Improve this break condition if it proves
// problematic
if self.get_relayer::<GetTaskStatusResponse>(&path).await.is_err() {
break;
}

Expand Down Expand Up @@ -319,10 +302,7 @@ fn build_auth_headers(key: &SecretSigningKey, req_bytes: &[u8]) -> Result<Header
let signature: Signature = root_key.sign(&payload);
let encoded_sig = b64_general_purpose::STANDARD_NO_PAD.encode(signature.to_bytes());

headers.insert(
RENEGADE_AUTH_HEADER_NAME,
HeaderValue::from_str(&encoded_sig).unwrap(),
);
headers.insert(RENEGADE_AUTH_HEADER_NAME, HeaderValue::from_str(&encoded_sig).unwrap());

Ok(headers)
}
5 changes: 0 additions & 5 deletions fee-sweeper/src/src/db/mod.rs

This file was deleted.

Loading

0 comments on commit 9e5d729

Please sign in to comment.