Skip to content

Commit

Permalink
feat: add offer expire datetime
Browse files Browse the repository at this point in the history
  • Loading branch information
crisdut committed Sep 23, 2023
1 parent fcf10e8 commit b828d63
Show file tree
Hide file tree
Showing 9 changed files with 76 additions and 43 deletions.
31 changes: 7 additions & 24 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ base85 = "2.0.0"
automerge = "0.5.1"
autosurgeon = "0.8"
baid58 = "0.4.4"
chrono = "0.4"

[target.'cfg(target_arch = "wasm32")'.dependencies]
bdk = { version = "0.28.2", features = [
Expand Down Expand Up @@ -127,6 +128,7 @@ tokio = { version = "1.28.2", features = ["full"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.36"


[build-dependencies]
anyhow = "1.0.71"
blake3 = "1.4.1"
Expand Down
32 changes: 25 additions & 7 deletions src/rgb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use rgbstd::{
use rgbwallet::{psbt::DbcPsbtError, RgbInvoice};
use std::{
collections::{BTreeMap, HashMap, HashSet},
ops::Sub,
str::FromStr,
};
use strict_encoding::{tn, StrictSerialize};
Expand Down Expand Up @@ -844,8 +845,8 @@ pub enum RgbSwapError {
NoContract,
/// Avaliable Utxo is required in this operation. {0}
NoUtxo(String),
/// Occurs an error in export step. {0}
Export(ExportContractError),
/// The Offer has expired.
OfferExpired,
/// Insufficient funds (expected: {input} sats / current: {output} sats)
Inflation {
/// Amount spent: input amounts
Expand All @@ -854,6 +855,8 @@ pub enum RgbSwapError {
/// Amount sent: sum of output value + transaction fee
output: u64,
},
/// Occurs an error in export step. {0}
Export(ExportContractError),
/// Occurs an error in create offer buyer step. {0}
Buyer(RgbOfferErrors),
/// Occurs an error in create step. {0}
Expand Down Expand Up @@ -925,6 +928,7 @@ pub async fn create_seller_offer(
bitcoin_price,
change_terminal,
iface,
expire_at,
..
} = request;

Expand Down Expand Up @@ -967,6 +971,7 @@ pub async fn create_seller_offer(
seller_address,
bitcoin_price,
seller_psbt.psbt.clone(),
expire_at,
);

let resp = RgbOfferResponse {
Expand Down Expand Up @@ -1101,15 +1106,25 @@ pub async fn create_buyer_bid(
let RgbOfferSwap {
iface,
public: offer_pub,
expire_at,
..
} = offer.clone();

let RgbBid {
bid_id,
offer_id,
asset_amount,
..
} = new_bid.clone();

if let Some(expire_at) = expire_at {
let utc = chrono::Local::now().naive_utc().timestamp();

if expire_at.sub(utc) <= 0 {
return Err(RgbSwapError::OfferExpired);
}
}

let invoice_req = InvoiceRequest {
iface,
contract_id: contract_id.to_string(),
Expand Down Expand Up @@ -1139,7 +1154,7 @@ pub async fn create_buyer_bid(
.map_err(RgbSwapError::IO)?;

let public_bid = RgbBidSwap::from(new_bid);
publish_swap_bid(sk, &offer_pub, public_bid.clone())
publish_swap_bid(sk, &offer_pub, public_bid.clone(), expire_at)
.await
.map_err(RgbSwapError::Marketplace)?;

Expand Down Expand Up @@ -1174,13 +1189,16 @@ pub async fn create_swap_transfer(
..
} = request.clone();

let RgbOfferSwap { iface, .. } = get_public_offer(offer_id.clone())
let RgbOfferSwap {
iface, expire_at, ..
} = get_public_offer(offer_id.clone())
.await
.map_err(RgbSwapError::Swap)?;

let RgbBidSwap { buyer_invoice, .. } = get_swap_bid(sk, offer_id.clone(), bid_id.clone())
.await
.map_err(RgbSwapError::Swap)?;
let RgbBidSwap { buyer_invoice, .. } =
get_swap_bid(sk, offer_id.clone(), bid_id.clone(), expire_at)
.await
.map_err(RgbSwapError::Swap)?;

let change_terminal = match iface.to_uppercase().as_str() {
"RGB20" => "/20/1",
Expand Down
14 changes: 12 additions & 2 deletions src/rgb/carbonado.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,16 @@ pub async fn store_public_offers(name: &str, changes: &[u8]) -> Result<(), Stora
pub async fn retrieve_swap_offer_bid(
sk: &str,
name: &str,
expire_at: Option<i64>,
) -> Result<LocalRgbOfferBid, StorageError> {
let hashed_name = blake3::hash(format!("{LIB_ID_RGB}-{name}").as_bytes())
let mut hashed_name = blake3::hash(format!("{LIB_ID_RGB}-{name}").as_bytes())
.to_hex()
.to_lowercase();

if let Some(expire_at) = expire_at {
hashed_name = format!("{hashed_name}-{expire_at}");
}

let main_name = &format!("{hashed_name}.c15");
let original_name = &format!("{hashed_name}-diff.c15");

Expand Down Expand Up @@ -484,11 +489,16 @@ pub async fn store_swap_offer_bid(
sk: &str,
name: &str,
changes: &[u8],
expire_at: Option<i64>,
) -> Result<(), StorageError> {
let hashed_name = blake3::hash(format!("{LIB_ID_RGB}-{name}").as_bytes())
let mut hashed_name = blake3::hash(format!("{LIB_ID_RGB}-{name}").as_bytes())
.to_hex()
.to_lowercase();

if let Some(expire_at) = expire_at {
hashed_name = format!("{hashed_name}-{expire_at}");
}

let main_name = &format!("{hashed_name}.c15");
let original_name = &format!("{hashed_name}-diff.c15");

Expand Down
6 changes: 4 additions & 2 deletions src/rgb/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ pub async fn retrieve_public_offers() -> Result<LocalRgbOffers, RgbPersistenceEr
pub async fn retrieve_swap_offer_bid(
sk: &str,
name: &str,
expire_at: Option<i64>,
) -> Result<LocalRgbOfferBid, RgbPersistenceError> {
let stock = retrieve_rgb_swap_offer_bid(sk, name)
let stock = retrieve_rgb_swap_offer_bid(sk, name, expire_at)
.await
.map_err(|op| RgbPersistenceError::RetrieveSwapBids(op.to_string()))?;

Expand Down Expand Up @@ -186,8 +187,9 @@ pub async fn store_swap_bids(
sk: &str,
name: &str,
changes: Vec<u8>,
expire_at: Option<i64>,
) -> Result<(), RgbPersistenceError> {
store_swap_offer_bid(sk, name, &changes)
store_swap_offer_bid(sk, name, &changes, expire_at)
.await
.map_err(|op| RgbPersistenceError::WriteSwapBids(op.to_string()))
}
Expand Down
19 changes: 12 additions & 7 deletions src/rgb/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub struct RgbOffer {
}

impl RgbOffer {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
secret: String,
contract_id: String,
Expand All @@ -101,6 +102,7 @@ impl RgbOffer {
seller_address: AddressCompat,
bitcoin_price: u64,
psbt: String,
expire_at: Option<i64>,
) -> Self {
let secp = Secp256k1::new();
let secret = hex::decode(secret).expect("");
Expand Down Expand Up @@ -138,6 +140,7 @@ impl RgbOffer {
seller_psbt: psbt,
seller_address: seller_address.to_string(),
public: public_key.to_hex(),
expire_at,
..Default::default()
}
}
Expand Down Expand Up @@ -422,6 +425,7 @@ pub async fn get_swap_bid(
sk: &str,
offer_id: String,
bid_id: BidId,
expire_at: Option<i64>,
) -> Result<RgbBidSwap, RgbOfferErrors> {
let bid = get_public_bid(offer_id.clone(), bid_id.clone()).await?;

Expand All @@ -433,11 +437,12 @@ pub async fn get_swap_bid(

let share_sk = SharedSecret::new(&public_key, &secret_key);
let share_sk = share_sk.display_secret().to_string();
let file_name = format!("{offer_id}-{bid_id}");

let LocalRgbOfferBid { rgb_bid, .. } = retrieve_swap_offer_bid(&share_sk, &file_name)
.await
.map_err(RgbOfferErrors::IO)?;
let file_name = format!("{offer_id}-{bid_id}");
let LocalRgbOfferBid { rgb_bid, .. } =
retrieve_swap_offer_bid(&share_sk, &file_name, expire_at)
.await
.map_err(RgbOfferErrors::IO)?;

Ok(rgb_bid)
}
Expand Down Expand Up @@ -513,6 +518,7 @@ pub async fn publish_swap_bid(
sk: &str,
offer_pub: &str,
new_bid: RgbBidSwap,
expire_at: Option<i64>,
) -> Result<(), RgbOfferErrors> {
let RgbBidSwap {
bid_id, offer_id, ..
Expand All @@ -527,8 +533,7 @@ pub async fn publish_swap_bid(
let share_sk = SharedSecret::new(&public_key, &secret_key);
let share_sk = share_sk.display_secret().to_string();
let file_name = format!("{offer_id}-{bid_id}");

let LocalRgbOfferBid { doc, .. } = retrieve_swap_offer_bid(&share_sk, &file_name)
let LocalRgbOfferBid { doc, .. } = retrieve_swap_offer_bid(&share_sk, &file_name, expire_at)
.await
.map_err(RgbOfferErrors::IO)?;

Expand All @@ -537,7 +542,7 @@ pub async fn publish_swap_bid(

reconcile(&mut local_copy, new_bid).map_err(|op| RgbOfferErrors::AutoMerge(op.to_string()))?;

store_swap_bids(&share_sk, &file_name, local_copy.save())
store_swap_bids(&share_sk, &file_name, local_copy.save(), expire_at)
.await
.map_err(RgbOfferErrors::IO)?;

Expand Down
2 changes: 2 additions & 0 deletions src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,8 @@ pub struct RgbOfferRequest {
/// Bitcoin Change Addresses (format: {address}:{amount})
#[garde(length(min = 0, max = 999))]
pub bitcoin_changes: Vec<String>,
#[garde(skip)]
pub expire_at: Option<i64>,
}

#[derive(Clone, Serialize, Deserialize, Debug, Display, Default)]
Expand Down
8 changes: 8 additions & 0 deletions tests/rgb/integration/swaps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ async fn create_scriptless_swap() -> anyhow::Result<()> {
let seller_sk = seller_keys.private.nostr_prv.clone();
let bitcoin_price: u64 = 100000;
let seller_asset_desc = seller_keys.public.rgb_assets_descriptor_xpub.clone();
let expire_at = (chrono::Local::now() + chrono::Duration::minutes(5))
.naive_utc()
.timestamp();
let seller_swap_req = RgbOfferRequest {
contract_id: contract_id.clone(),
iface,
Expand All @@ -144,6 +147,7 @@ async fn create_scriptless_swap() -> anyhow::Result<()> {
descriptor: SecretString(seller_asset_desc),
change_terminal: "/20/1".to_string(),
bitcoin_changes: vec![],
expire_at: Some(expire_at),
};

let seller_swap_resp = create_seller_offer(&seller_sk, seller_swap_req).await;
Expand Down Expand Up @@ -364,6 +368,9 @@ async fn create_scriptless_swap_for_uda() -> anyhow::Result<()> {
let seller_sk = seller_keys.private.nostr_prv.clone();
let bitcoin_price: u64 = 100000;
let seller_asset_desc = seller_keys.public.rgb_udas_descriptor_xpub.clone();
let expire_at = (chrono::Local::now() + chrono::Duration::minutes(5))
.naive_utc()
.timestamp();
let seller_swap_req = RgbOfferRequest {
contract_id: contract_id.clone(),
iface,
Expand All @@ -372,6 +379,7 @@ async fn create_scriptless_swap_for_uda() -> anyhow::Result<()> {
descriptor: SecretString(seller_asset_desc),
change_terminal: "/21/1".to_string(),
bitcoin_changes: vec![],
expire_at: Some(expire_at),
};

let seller_swap_resp = create_seller_offer(&seller_sk, seller_swap_req).await;
Expand Down
Loading

0 comments on commit b828d63

Please sign in to comment.