Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
4 changes: 3 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ CARBONADO_ENDPOINT=http://localhost:7070/carbonado
UDAS_UTXO=3b367e1facc3174e97658295961faf6a4ed889129c881b7a73db1f074b49bd8a:
MARKETPLACE_SEED="lion bronze dumb tuna perfect fantasy wall orphan improve business harbor sadness"
MARKETPLACE_NOSTR=cd591c134a0d88991326b1619953d0eae2287d315a7c4a93c1e4883a8c26c464

# 1..100
MARKETPLACE_FEE_PERC=
# xpub..
MARKETPLACE_FEE_XPUB=

# :: Coordinator ::
COORDINATOR_NOSTR=9e8294eb38ba77c0fba982da8fbd370b8868c6dbfc9ca414aff4863c15dfbcff

# :: RGB PROXY ::
RGB_PROXY_ENDPOINT=http://localhost:3001
70 changes: 66 additions & 4 deletions src/bin/bitmaskd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ use bitcoin_30::secp256k1::{ecdh::SharedSecret, PublicKey, SecretKey};
use bitmask_core::{
bitcoin::{save_mnemonic, sign_and_publish_psbt_file},
carbonado::{
handle_file,
handle_file, marketplace_retrieve, marketplace_store,
metrics::{metrics, metrics_csv},
server_retrieve, server_store, store,
store,
},
constants::{
get_marketplace_nostr_key, get_marketplace_seed, get_network, get_udas_utxo, switch_network,
Expand Down Expand Up @@ -524,7 +524,7 @@ async fn co_server_store(
body: Bytes,
) -> Result<impl IntoResponse, AppError> {
info!("POST /carbonado/server/{name}, {} bytes", body.len());
let (filepath, encoded) = server_store(&name, &body, None).await?;
let (filepath, encoded) = marketplace_store(&name, &body, None).await?;

match OpenOptions::new()
.read(true)
Expand Down Expand Up @@ -624,7 +624,7 @@ async fn co_metadata(
async fn co_server_retrieve(Path(name): Path<String>) -> Result<impl IntoResponse, AppError> {
info!("GET /server/{name}");

let result = server_retrieve(&name).await;
let result = marketplace_retrieve(&name).await;
let cc = CacheControl::new().with_no_cache();

match result {
Expand Down Expand Up @@ -684,6 +684,56 @@ async fn rgb_proxy_media_data_save(
Ok((StatusCode::OK, Json(resp)))
}

async fn rgb_auction_get_offer(
Path(offer_id): Path<String>,
Json(_request): Json<String>,
) -> Result<impl IntoResponse, AppError> {
info!("GET /auction/{offer_id}");
Ok((StatusCode::OK, Json("")))
}

async fn rgb_auction_create_offer(
TypedHeader(_auth): TypedHeader<Authorization<Bearer>>,
Path(offer_id): Path<String>,
Json(_request): Json<String>,
) -> Result<impl IntoResponse, AppError> {
info!("POST /auction/{offer_id}");
Ok((StatusCode::OK, Json("")))
}

async fn rgb_auction_destroy_offer(
TypedHeader(_auth): TypedHeader<Authorization<Bearer>>,
Path(offer_id): Path<String>,
) -> Result<impl IntoResponse, AppError> {
info!("DELETE /auction/{offer_id}");
Ok((StatusCode::OK, Json("")))
}

async fn rgb_auction_get_bid(
Path((offer_id, bid_id)): Path<(String, String)>,
Json(_request): Json<String>,
) -> Result<impl IntoResponse, AppError> {
info!("GET /auction/{offer_id}/{bid_id}");
Ok((StatusCode::OK, Json("")))
}

async fn rgb_auction_create_bid(
TypedHeader(_auth): TypedHeader<Authorization<Bearer>>,
Path((offer_id, bid_id)): Path<(String, String)>,
Json(_request): Json<String>,
) -> Result<impl IntoResponse, AppError> {
info!("POST /auction/{offer_id}/{bid_id}");
Ok((StatusCode::OK, Json("")))
}

async fn rgb_auction_destroy_bid(
TypedHeader(_auth): TypedHeader<Authorization<Bearer>>,
Path((offer_id, bid_id)): Path<(String, String)>,
) -> Result<impl IntoResponse, AppError> {
info!("DELETE /auction/{offer_id}/{bid_id}");
Ok((StatusCode::OK, Json("")))
}

const BMC_VERSION: &str = env!("CARGO_PKG_VERSION");

async fn status() -> Result<impl IntoResponse, AppError> {
Expand Down Expand Up @@ -822,6 +872,18 @@ async fn main() -> Result<()> {
.route("/proxy/media-metadata", post(rgb_proxy_media_data_save))
.route("/proxy/media-metadata/:id", get(rgb_proxy_media_retrieve))
.route("/proxy/media/:id", get(rgb_proxy_metadata_retrieve))
.route("/auctions/:offer_id", get(rgb_auction_get_offer))
.route("/auctions/:offer_id", post(rgb_auction_create_offer))
.route("/auctions/:offer_id", delete(rgb_auction_destroy_offer))
.route("/auctions/:offer_id/bid/:bid_id", get(rgb_auction_get_bid))
.route(
"/auctions/:offer_id/bid/:bid_id",
post(rgb_auction_create_bid),
)
.route(
"/auction/:offer_id/bid/:bid_id",
delete(rgb_auction_destroy_bid),
)
.route("/metrics.json", get(json_metrics))
.route("/metrics.csv", get(csv_metrics));

Expand Down
8 changes: 7 additions & 1 deletion src/bitcoin/psbt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ use crate::{

#[derive(Error, Debug)]
pub enum BitcoinPsbtError {
/// Could not broadcast PSBT
#[error("Could not broadcast PSBT")]
CouldNotBroadcastPsbt(String),
/// Could not finalize when signing PSBT
#[error("Could not finalize when signing PSBT")]
CouldNotFinalizePsbt,
Expand Down Expand Up @@ -70,7 +73,10 @@ pub async fn publish_psbt(
let tx = psbt.extract_tx();
debug!("tx:", &serialize(&tx.clone()).to_hex());
let blockchain = get_blockchain().await;
blockchain.broadcast(&tx).await?;
blockchain
.broadcast(&tx)
.await
.map_err(|op| BitcoinPsbtError::CouldNotBroadcastPsbt(op.to_string()))?;

let txid = tx.txid();
let tx = blockchain.get_tx(&txid).await?;
Expand Down
115 changes: 108 additions & 7 deletions src/carbonado.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@ pub mod error;
pub mod metrics;

#[cfg(not(target_arch = "wasm32"))]
pub use server::{handle_file, retrieve, retrieve_metadata, server_retrieve, server_store, store};
pub use server::{
auctions_retrieve, auctions_store, handle_file, marketplace_retrieve, marketplace_store,
retrieve, retrieve_metadata, store,
};

#[cfg(not(target_arch = "wasm32"))]
mod server {
use crate::constants::get_marketplace_nostr_key;
use crate::constants::{get_coordinator_nostr_key, get_marketplace_nostr_key};

use super::*;

use std::{
io::{Error, ErrorKind},
path::PathBuf,
str::FromStr,
};

use bitcoin_30::secp256k1::ecdh::SharedSecret;
use tokio::fs;

pub async fn store(
Expand Down Expand Up @@ -51,7 +56,7 @@ mod server {
Ok(())
}

pub async fn server_store(
pub async fn marketplace_store(
name: &str,
input: &[u8],
metadata: Option<Vec<u8>>,
Expand All @@ -78,6 +83,43 @@ mod server {
Ok((filepath, body))
}

pub async fn auctions_store(
Copy link
Member

Choose a reason for hiding this comment

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

Is there any way to abstract this out so it's not in src/carbonado.rs methods? Ideally this file would be cleaner and simpler, more focused and have less duplication.

Copy link
Contributor Author

@crisdut crisdut Dec 12, 2023

Choose a reason for hiding this comment

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

I believe so, but I feel like it will lose some of its purpose. I centralized in this file all the calls that are sometimes made by the client and sometimes made by the server.

But yes, I can carry out another separation.

bundle_id: &str,
name: &str,
input: &[u8],
metadata: Option<Vec<u8>>,
) -> Result<(PathBuf, Vec<u8>), CarbonadoError> {
let coordinator_key: String = get_coordinator_nostr_key().await;

let level = 15;
let coordinator_sk = hex::decode(coordinator_key)?;
let coordinator_secret_key = SecretKey::from_slice(&coordinator_sk)?;
let bundle_public_key =
PublicKey::from_str(bundle_id).map_err(|_| CarbonadoError::WrongNostrPublicKey)?;

let share_sk = SharedSecret::new(&bundle_public_key, &coordinator_secret_key);
let share_sk = share_sk.display_secret().to_string();

let sk = hex::decode(share_sk)?;
let secret_key = SecretKey::from_slice(&sk)?;
let public_key = PublicKey::from_secret_key_global(&secret_key);

let pk = public_key.serialize();
let pk_hex = public_key.to_hex();

let mut meta: Option<[u8; 8]> = default!();
if let Some(metadata) = metadata {
let mut inner: [u8; 8] = default!();
inner[..metadata.len()].copy_from_slice(&metadata);
meta = Some(inner);
}

let (body, _encode_info) = carbonado::file::encode(&sk, Some(&pk), input, level, meta)?;
let filepath = handle_file(&pk_hex, name, body.len()).await?;
fs::write(filepath.clone(), body.clone()).await?;
Ok((filepath, body))
}

pub async fn retrieve(
sk: &str,
name: &str,
Expand Down Expand Up @@ -120,7 +162,9 @@ mod server {
Ok((Vec::new(), None))
}

pub async fn server_retrieve(name: &str) -> Result<(Vec<u8>, Option<Vec<u8>>), CarbonadoError> {
pub async fn marketplace_retrieve(
name: &str,
) -> Result<(Vec<u8>, Option<Vec<u8>>), CarbonadoError> {
let marketplace_key: String = get_marketplace_nostr_key().await;

let sk = hex::decode(marketplace_key)?;
Expand All @@ -144,6 +188,42 @@ mod server {
Ok((Vec::new(), None))
}

pub async fn auctions_retrieve(
bundle_id: &str,
name: &str,
) -> Result<(Vec<u8>, Option<Vec<u8>>), CarbonadoError> {
let coordinator_key: String = get_coordinator_nostr_key().await;

let coordinator_sk = hex::decode(coordinator_key)?;
let coordinator_secret_key = SecretKey::from_slice(&coordinator_sk)?;
let bundle_public_key =
PublicKey::from_str(bundle_id).map_err(|_| CarbonadoError::WrongNostrPublicKey)?;

let share_sk = SharedSecret::new(&bundle_public_key, &coordinator_secret_key);
let share_sk = share_sk.display_secret().to_string();

let sk = hex::decode(share_sk)?;
let secret_key = SecretKey::from_slice(&sk)?;
let public_key = PublicKey::from_secret_key_global(&secret_key);

let pk = public_key.to_hex();

let mut final_name = name.to_string();
let network = NETWORK.read().await.to_string();
let networks = ["bitcoin", "testnet", "signet", "regtest"];
if !networks.into_iter().any(|x| name.contains(x)) {
final_name = format!("{network}-{name}");
}

let filepath = handle_file(&pk, &final_name, 0).await?;
if let Ok(bytes) = fs::read(filepath).await {
let (header, decoded) = carbonado::file::decode(&sk, &bytes)?;
return Ok((decoded, header.metadata.map(|m| m.to_vec())));
}

Ok((Vec::new(), None))
}

pub async fn handle_file(
pk: &str,
name: &str,
Expand Down Expand Up @@ -210,7 +290,10 @@ mod server {
}

#[cfg(target_arch = "wasm32")]
pub use client::{retrieve, retrieve_metadata, server_retrieve, server_store, store};
pub use client::{
auctions_retrieve, auctions_store, marketplace_retrieve, marketplace_store, retrieve,
retrieve_metadata, store,
};

#[cfg(target_arch = "wasm32")]
mod client {
Expand Down Expand Up @@ -296,7 +379,7 @@ mod client {
}
}

pub async fn server_store(
pub async fn marketplace_store(
name: &str,
input: &[u8],
_metadata: Option<Vec<u8>>,
Expand Down Expand Up @@ -328,6 +411,15 @@ mod client {
}
}

pub async fn auctions_store(
_bundle_id: &str,
_name: &str,
_input: &[u8],
_metadata: Option<Vec<u8>>,
) -> Result<(), CarbonadoError> {
todo!()
}

pub async fn retrieve_metadata(sk: &str, name: &str) -> Result<FileMetadata, CarbonadoError> {
let sk = hex::decode(sk)?;
let secret_key = SecretKey::from_slice(&sk)?;
Expand Down Expand Up @@ -416,7 +508,9 @@ mod client {
Ok((Vec::new(), None))
}

pub async fn server_retrieve(name: &str) -> Result<(Vec<u8>, Option<Vec<u8>>), CarbonadoError> {
pub async fn marketplace_retrieve(
name: &str,
) -> Result<(Vec<u8>, Option<Vec<u8>>), CarbonadoError> {
let network = NETWORK.read().await.to_string();
let endpoints = CARBONADO_ENDPOINT.read().await.to_string();
let endpoints: Vec<&str> = endpoints.split(',').collect();
Expand All @@ -438,6 +532,13 @@ mod client {
Ok((encoded.to_vec(), None))
}

pub async fn auctions_retrieve(
_bundle_id: &str,
_name: &str,
) -> Result<(Vec<u8>, Option<Vec<u8>>), CarbonadoError> {
todo!()
}

async fn fetch_post(url: String, body: Arc<Vec<u8>>) -> Result<JsValue, JsValue> {
let array = Uint8Array::new_with_length(body.len() as u32);
array.copy_from(&body);
Expand Down
2 changes: 2 additions & 0 deletions src/carbonado/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pub enum CarbonadoError {
AllEndpointsFailed,
/// Wrong Nostr private key
WrongNostrPrivateKey,
/// Wrong Nostr public key
WrongNostrPublicKey,
/// Debug: {0}
Debug(String),
}
8 changes: 7 additions & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ pub static MARKETPLACE_FEE_PERC: Lazy<RwLock<String>> =
pub static MARKETPLACE_FEE_XPUB: Lazy<RwLock<String>> =
Lazy::new(|| RwLock::new(dot_env("MARKETPLACE_FEE_XPUB")));

pub static COORDINATOR_NOSTR: Lazy<RwLock<String>> =
Lazy::new(|| RwLock::new(dot_env("COORDINATOR_NOSTR")));

pub async fn get_marketplace_seed() -> String {
MARKETPLACE_SEED.read().await.to_string()
}
Expand All @@ -76,6 +79,10 @@ pub async fn get_marketplace_fee_xpub() -> String {
MARKETPLACE_FEE_XPUB.read().await.to_string()
}

pub async fn get_coordinator_nostr_key() -> String {
COORDINATOR_NOSTR.read().await.to_string()
}

pub static UDAS_UTXO: Lazy<RwLock<String>> = Lazy::new(|| RwLock::new(dot_env("UDAS_UTXO")));

pub async fn get_udas_utxo() -> String {
Expand Down Expand Up @@ -220,5 +227,4 @@ pub mod storage_keys {
pub const ASSETS_OFFERS: &str = "bitmask-asset_offers.c15";
pub const ASSETS_BIDS: &str = "bitmask-asset_bids.c15";
pub const MARKETPLACE_OFFERS: &str = "bitmask-marketplace_public_offers.c15";
pub const MARKETPLACE_BIDS: &str = "bitmask-marketplace_public_bids.c15";
}
Loading