Simplify MintUrl, use it directly in wallet/client.rs

This commit is contained in:
ok300
2024-10-20 19:33:22 +02:00
parent f6533a08de
commit 98c94e28bb
12 changed files with 52 additions and 122 deletions

View File

@@ -6,7 +6,6 @@ use url::Url;
#[derive(Args)] #[derive(Args)]
pub struct MintInfoSubcommand { pub struct MintInfoSubcommand {
/// Cashu Token
mint_url: MintUrl, mint_url: MintUrl,
} }
@@ -17,7 +16,7 @@ pub async fn mint_info(proxy: Option<Url>, sub_command_args: &MintInfoSubcommand
}; };
let info = client let info = client
.get_mint_info(sub_command_args.mint_url.clone().try_into()?) .get_mint_info(sub_command_args.mint_url.clone())
.await?; .await?;
println!("{:#?}", info); println!("{:#?}", info);

View File

@@ -6,7 +6,7 @@
use core::fmt; use core::fmt;
use core::str::FromStr; use core::str::FromStr;
use serde::{Deserialize, Deserializer, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
use url::{ParseError, Url}; use url::{ParseError, Url};
@@ -22,7 +22,7 @@ pub enum Error {
} }
/// MintUrl Url /// MintUrl Url
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct MintUrl(String); pub struct MintUrl(String);
impl MintUrl { impl MintUrl {
@@ -60,25 +60,16 @@ impl MintUrl {
Ok(formatted_url) Ok(formatted_url)
} }
/// Empty mint url
pub fn empty() -> Self {
Self(String::new())
}
/// Join onto url /// Join onto url
pub fn join(&self, path: &str) -> Result<Url, Error> { pub fn join(&self, path: &str) -> Result<Url, Error> {
let url: Url = self.try_into()?; Url::parse(&self.0)
Ok(url.join(path)?) .and_then(|url| url.join(path))
.map_err(Into::into)
} }
}
impl<'de> Deserialize<'de> for MintUrl { /// Append path elements onto the URL
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> pub fn join_paths(&self, path_elements: &[&str]) -> Result<Url, Error> {
where self.join(&path_elements.join("/"))
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
MintUrl::from_str(&s).map_err(serde::de::Error::custom)
} }
} }
@@ -94,22 +85,6 @@ impl FromStr for MintUrl {
} }
} }
impl TryFrom<MintUrl> for Url {
type Error = Error;
fn try_from(mint_url: MintUrl) -> Result<Url, Self::Error> {
Ok(Self::parse(&mint_url.0)?)
}
}
impl TryFrom<&MintUrl> for Url {
type Error = Error;
fn try_from(mint_url: &MintUrl) -> Result<Url, Self::Error> {
Ok(Self::parse(mint_url.0.as_str())?)
}
}
impl fmt::Display for MintUrl { impl fmt::Display for MintUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0) write!(f, "{}", self.0)

View File

@@ -66,9 +66,6 @@ pub enum Error {
/// Unsupported token /// Unsupported token
#[error("Unsupported payment method")] #[error("Unsupported payment method")]
UnsupportedPaymentMethod, UnsupportedPaymentMethod,
/// Invalid Url
#[error("Invalid URL")]
InvalidUrl,
/// Serde Json error /// Serde Json error
#[error(transparent)] #[error(transparent)]
SerdeJsonError(#[from] serde_json::Error), SerdeJsonError(#[from] serde_json::Error),
@@ -78,9 +75,6 @@ pub enum Error {
/// Base64 error /// Base64 error
#[error(transparent)] #[error(transparent)]
Base64Error(#[from] bitcoin::base64::DecodeError), Base64Error(#[from] bitcoin::base64::DecodeError),
/// Parse Url Error
#[error(transparent)]
UrlParseError(#[from] url::ParseError),
/// Ciborium error /// Ciborium error
#[error(transparent)] #[error(transparent)]
CiboriumError(#[from] ciborium::de::Error<std::io::Error>), CiboriumError(#[from] ciborium::de::Error<std::io::Error>),

View File

@@ -9,7 +9,6 @@ use std::str::FromStr;
use bitcoin::base64::engine::{general_purpose, GeneralPurpose}; use bitcoin::base64::engine::{general_purpose, GeneralPurpose};
use bitcoin::base64::{alphabet, Engine as _}; use bitcoin::base64::{alphabet, Engine as _};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
use super::{Error, Proof, ProofV4, Proofs}; use super::{Error, Proof, ProofV4, Proofs};
use crate::mint_url::MintUrl; use crate::mint_url::MintUrl;
@@ -181,9 +180,6 @@ impl TokenV3 {
return Err(Error::ProofsRequired); return Err(Error::ProofsRequired);
} }
// Check Url is valid
let _: Url = (&mint_url).try_into().map_err(|_| Error::InvalidUrl)?;
Ok(Self { Ok(Self {
token: vec![TokenV3Token::new(mint_url, proofs)], token: vec![TokenV3Token::new(mint_url, proofs)],
memo, memo,

View File

@@ -7,6 +7,7 @@ use url::Url;
use super::Error; use super::Error;
use crate::error::ErrorResponse; use crate::error::ErrorResponse;
use crate::mint_url::MintUrl;
use crate::nuts::nut15::Mpp; use crate::nuts::nut15::Mpp;
use crate::nuts::{ use crate::nuts::{
BlindedMessage, CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysResponse, BlindedMessage, CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysResponse,
@@ -17,24 +18,6 @@ use crate::nuts::{
}; };
use crate::{Amount, Bolt11Invoice}; use crate::{Amount, Bolt11Invoice};
fn join_url(url: Url, paths: &[&str]) -> Result<Url, Error> {
let mut url = url;
for path in paths {
if !url.path().ends_with('/') {
url.path_segments_mut()
.map_err(|_| Error::UrlPathSegments)?
.push(path);
} else {
url.path_segments_mut()
.map_err(|_| Error::UrlPathSegments)?
.pop()
.push(path);
}
}
Ok(url)
}
/// Http Client /// Http Client
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct HttpClient { pub struct HttpClient {
@@ -87,8 +70,8 @@ impl HttpClient {
/// Get Active Mint Keys [NUT-01] /// Get Active Mint Keys [NUT-01]
#[instrument(skip(self), fields(mint_url = %mint_url))] #[instrument(skip(self), fields(mint_url = %mint_url))]
pub async fn get_mint_keys(&self, mint_url: Url) -> Result<Vec<KeySet>, Error> { pub async fn get_mint_keys(&self, mint_url: MintUrl) -> Result<Vec<KeySet>, Error> {
let url = join_url(mint_url, &["v1", "keys"])?; let url = mint_url.join_paths(&["v1", "keys"])?;
let keys = self.inner.get(url).send().await?.json::<Value>().await?; let keys = self.inner.get(url).send().await?.json::<Value>().await?;
match serde_json::from_value::<KeysResponse>(keys.clone()) { match serde_json::from_value::<KeysResponse>(keys.clone()) {
@@ -99,8 +82,8 @@ impl HttpClient {
/// Get Keyset Keys [NUT-01] /// Get Keyset Keys [NUT-01]
#[instrument(skip(self), fields(mint_url = %mint_url))] #[instrument(skip(self), fields(mint_url = %mint_url))]
pub async fn get_mint_keyset(&self, mint_url: Url, keyset_id: Id) -> Result<KeySet, Error> { pub async fn get_mint_keyset(&self, mint_url: MintUrl, keyset_id: Id) -> Result<KeySet, Error> {
let url = join_url(mint_url, &["v1", "keys", &keyset_id.to_string()])?; let url = mint_url.join_paths(&["v1", "keys", &keyset_id.to_string()])?;
let keys = self.inner.get(url).send().await?.json::<Value>().await?; let keys = self.inner.get(url).send().await?.json::<Value>().await?;
match serde_json::from_value::<KeysResponse>(keys.clone()) { match serde_json::from_value::<KeysResponse>(keys.clone()) {
@@ -111,8 +94,8 @@ impl HttpClient {
/// Get Keysets [NUT-02] /// Get Keysets [NUT-02]
#[instrument(skip(self), fields(mint_url = %mint_url))] #[instrument(skip(self), fields(mint_url = %mint_url))]
pub async fn get_mint_keysets(&self, mint_url: Url) -> Result<KeysetResponse, Error> { pub async fn get_mint_keysets(&self, mint_url: MintUrl) -> Result<KeysetResponse, Error> {
let url = join_url(mint_url, &["v1", "keysets"])?; let url = mint_url.join_paths(&["v1", "keysets"])?;
let res = self.inner.get(url).send().await?.json::<Value>().await?; let res = self.inner.get(url).send().await?.json::<Value>().await?;
match serde_json::from_value::<KeysetResponse>(res.clone()) { match serde_json::from_value::<KeysetResponse>(res.clone()) {
@@ -125,12 +108,12 @@ impl HttpClient {
#[instrument(skip(self), fields(mint_url = %mint_url))] #[instrument(skip(self), fields(mint_url = %mint_url))]
pub async fn post_mint_quote( pub async fn post_mint_quote(
&self, &self,
mint_url: Url, mint_url: MintUrl,
amount: Amount, amount: Amount,
unit: CurrencyUnit, unit: CurrencyUnit,
description: Option<String>, description: Option<String>,
) -> Result<MintQuoteBolt11Response, Error> { ) -> Result<MintQuoteBolt11Response, Error> {
let url = join_url(mint_url, &["v1", "mint", "quote", "bolt11"])?; let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11"])?;
let request = MintQuoteBolt11Request { let request = MintQuoteBolt11Request {
amount, amount,
@@ -160,10 +143,10 @@ impl HttpClient {
#[instrument(skip(self), fields(mint_url = %mint_url))] #[instrument(skip(self), fields(mint_url = %mint_url))]
pub async fn get_mint_quote_status( pub async fn get_mint_quote_status(
&self, &self,
mint_url: Url, mint_url: MintUrl,
quote_id: &str, quote_id: &str,
) -> Result<MintQuoteBolt11Response, Error> { ) -> Result<MintQuoteBolt11Response, Error> {
let url = join_url(mint_url, &["v1", "mint", "quote", "bolt11", quote_id])?; let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?;
let res = self.inner.get(url).send().await?.json::<Value>().await?; let res = self.inner.get(url).send().await?.json::<Value>().await?;
@@ -180,11 +163,11 @@ impl HttpClient {
#[instrument(skip(self, quote, premint_secrets), fields(mint_url = %mint_url))] #[instrument(skip(self, quote, premint_secrets), fields(mint_url = %mint_url))]
pub async fn post_mint( pub async fn post_mint(
&self, &self,
mint_url: Url, mint_url: MintUrl,
quote: &str, quote: &str,
premint_secrets: PreMintSecrets, premint_secrets: PreMintSecrets,
) -> Result<MintBolt11Response, Error> { ) -> Result<MintBolt11Response, Error> {
let url = join_url(mint_url, &["v1", "mint", "bolt11"])?; let url = mint_url.join_paths(&["v1", "mint", "bolt11"])?;
let request = MintBolt11Request { let request = MintBolt11Request {
quote: quote.to_string(), quote: quote.to_string(),
@@ -210,12 +193,12 @@ impl HttpClient {
#[instrument(skip(self, request), fields(mint_url = %mint_url))] #[instrument(skip(self, request), fields(mint_url = %mint_url))]
pub async fn post_melt_quote( pub async fn post_melt_quote(
&self, &self,
mint_url: Url, mint_url: MintUrl,
unit: CurrencyUnit, unit: CurrencyUnit,
request: Bolt11Invoice, request: Bolt11Invoice,
mpp_amount: Option<Amount>, mpp_amount: Option<Amount>,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response, Error> {
let url = join_url(mint_url, &["v1", "melt", "quote", "bolt11"])?; let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11"])?;
let options = mpp_amount.map(|amount| Mpp { amount }); let options = mpp_amount.map(|amount| Mpp { amount });
@@ -244,10 +227,10 @@ impl HttpClient {
#[instrument(skip(self), fields(mint_url = %mint_url))] #[instrument(skip(self), fields(mint_url = %mint_url))]
pub async fn get_melt_quote_status( pub async fn get_melt_quote_status(
&self, &self,
mint_url: Url, mint_url: MintUrl,
quote_id: &str, quote_id: &str,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response, Error> {
let url = join_url(mint_url, &["v1", "melt", "quote", "bolt11", quote_id])?; let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?;
let res = self.inner.get(url).send().await?.json::<Value>().await?; let res = self.inner.get(url).send().await?.json::<Value>().await?;
@@ -262,12 +245,12 @@ impl HttpClient {
#[instrument(skip(self, quote, inputs, outputs), fields(mint_url = %mint_url))] #[instrument(skip(self, quote, inputs, outputs), fields(mint_url = %mint_url))]
pub async fn post_melt( pub async fn post_melt(
&self, &self,
mint_url: Url, mint_url: MintUrl,
quote: String, quote: String,
inputs: Vec<Proof>, inputs: Vec<Proof>,
outputs: Option<Vec<BlindedMessage>>, outputs: Option<Vec<BlindedMessage>>,
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response, Error> {
let url = join_url(mint_url, &["v1", "melt", "bolt11"])?; let url = mint_url.join_paths(&["v1", "melt", "bolt11"])?;
let request = MeltBolt11Request { let request = MeltBolt11Request {
quote, quote,
@@ -299,10 +282,10 @@ impl HttpClient {
#[instrument(skip(self, swap_request), fields(mint_url = %mint_url))] #[instrument(skip(self, swap_request), fields(mint_url = %mint_url))]
pub async fn post_swap( pub async fn post_swap(
&self, &self,
mint_url: Url, mint_url: MintUrl,
swap_request: SwapRequest, swap_request: SwapRequest,
) -> Result<SwapResponse, Error> { ) -> Result<SwapResponse, Error> {
let url = join_url(mint_url, &["v1", "swap"])?; let url = mint_url.join_paths(&["v1", "swap"])?;
let res = self let res = self
.inner .inner
@@ -321,8 +304,8 @@ impl HttpClient {
/// Get Mint Info [NUT-06] /// Get Mint Info [NUT-06]
#[instrument(skip(self), fields(mint_url = %mint_url))] #[instrument(skip(self), fields(mint_url = %mint_url))]
pub async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error> { pub async fn get_mint_info(&self, mint_url: MintUrl) -> Result<MintInfo, Error> {
let url = join_url(mint_url, &["v1", "info"])?; let url = mint_url.join_paths(&["v1", "info"])?;
let res = self.inner.get(url).send().await?.json::<Value>().await?; let res = self.inner.get(url).send().await?.json::<Value>().await?;
@@ -339,10 +322,10 @@ impl HttpClient {
#[instrument(skip(self), fields(mint_url = %mint_url))] #[instrument(skip(self), fields(mint_url = %mint_url))]
pub async fn post_check_state( pub async fn post_check_state(
&self, &self,
mint_url: Url, mint_url: MintUrl,
ys: Vec<PublicKey>, ys: Vec<PublicKey>,
) -> Result<CheckStateResponse, Error> { ) -> Result<CheckStateResponse, Error> {
let url = join_url(mint_url, &["v1", "checkstate"])?; let url = mint_url.join_paths(&["v1", "checkstate"])?;
let request = CheckStateRequest { ys }; let request = CheckStateRequest { ys };
let res = self let res = self
@@ -364,10 +347,10 @@ impl HttpClient {
#[instrument(skip(self, request), fields(mint_url = %mint_url))] #[instrument(skip(self, request), fields(mint_url = %mint_url))]
pub async fn post_restore( pub async fn post_restore(
&self, &self,
mint_url: Url, mint_url: MintUrl,
request: RestoreRequest, request: RestoreRequest,
) -> Result<RestoreResponse, Error> { ) -> Result<RestoreResponse, Error> {
let url = join_url(mint_url, &["v1", "restore"])?; let url = mint_url.join_paths(&["v1", "restore"])?;
let res = self let res = self
.inner .inner

View File

@@ -18,7 +18,7 @@ impl Wallet {
} else { } else {
let keys = self let keys = self
.client .client
.get_mint_keyset(self.mint_url.clone().try_into()?, keyset_id) .get_mint_keyset(self.mint_url.clone(), keyset_id)
.await?; .await?;
self.localstore.add_keys(keys.keys.clone()).await?; self.localstore.add_keys(keys.keys.clone()).await?;
@@ -34,10 +34,7 @@ impl Wallet {
/// Queries mint for all keysets /// Queries mint for all keysets
#[instrument(skip(self))] #[instrument(skip(self))]
pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> { pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
let keysets = self let keysets = self.client.get_mint_keysets(self.mint_url.clone()).await?;
.client
.get_mint_keysets(self.mint_url.clone().try_into()?)
.await?;
self.localstore self.localstore
.add_mint_keysets(self.mint_url.clone(), keysets.keysets.clone()) .add_mint_keysets(self.mint_url.clone(), keysets.keysets.clone())
@@ -52,10 +49,7 @@ impl Wallet {
/// keysets /// keysets
#[instrument(skip(self))] #[instrument(skip(self))]
pub async fn get_active_mint_keyset(&self) -> Result<KeySetInfo, Error> { pub async fn get_active_mint_keyset(&self) -> Result<KeySetInfo, Error> {
let keysets = self let keysets = self.client.get_mint_keysets(self.mint_url.clone()).await?;
.client
.get_mint_keysets(self.mint_url.clone().try_into()?)
.await?;
let keysets = keysets.keysets; let keysets = keysets.keysets;
self.localstore self.localstore

View File

@@ -59,7 +59,7 @@ impl Wallet {
let quote_res = self let quote_res = self
.client .client
.post_melt_quote(self.mint_url.clone().try_into()?, self.unit, invoice, mpp) .post_melt_quote(self.mint_url.clone(), self.unit, invoice, mpp)
.await?; .await?;
if quote_res.amount != amount { if quote_res.amount != amount {
@@ -90,7 +90,7 @@ impl Wallet {
) -> Result<MeltQuoteBolt11Response, Error> { ) -> Result<MeltQuoteBolt11Response, Error> {
let response = self let response = self
.client .client
.get_melt_quote_status(self.mint_url.clone().try_into()?, quote_id) .get_melt_quote_status(self.mint_url.clone(), quote_id)
.await?; .await?;
match self.localstore.get_melt_quote(quote_id).await? { match self.localstore.get_melt_quote(quote_id).await? {
@@ -149,7 +149,7 @@ impl Wallet {
let melt_response = self let melt_response = self
.client .client
.post_melt( .post_melt(
self.mint_url.clone().try_into()?, self.mint_url.clone(),
quote_id.to_string(), quote_id.to_string(),
proofs.clone(), proofs.clone(),
Some(premint_secrets.blinded_messages()), Some(premint_secrets.blinded_messages()),

View File

@@ -67,7 +67,7 @@ impl Wallet {
let quote_res = self let quote_res = self
.client .client
.post_mint_quote(mint_url.clone().try_into()?, amount, unit, description) .post_mint_quote(mint_url.clone(), amount, unit, description)
.await?; .await?;
let quote = MintQuote { let quote = MintQuote {
@@ -90,7 +90,7 @@ impl Wallet {
pub async fn mint_quote_state(&self, quote_id: &str) -> Result<MintQuoteBolt11Response, Error> { pub async fn mint_quote_state(&self, quote_id: &str) -> Result<MintQuoteBolt11Response, Error> {
let response = self let response = self
.client .client
.get_mint_quote_status(self.mint_url.clone().try_into()?, quote_id) .get_mint_quote_status(self.mint_url.clone(), quote_id)
.await?; .await?;
match self.localstore.get_mint_quote(quote_id).await? { match self.localstore.get_mint_quote(quote_id).await? {
@@ -215,11 +215,7 @@ impl Wallet {
let mint_res = self let mint_res = self
.client .client
.post_mint( .post_mint(self.mint_url.clone(), quote_id, premint_secrets.clone())
self.mint_url.clone().try_into()?,
quote_id,
premint_secrets.clone(),
)
.await?; .await?;
let keys = self.get_keyset_keys(active_keyset_id).await?; let keys = self.get_keyset_keys(active_keyset_id).await?;

View File

@@ -161,11 +161,7 @@ impl Wallet {
/// Qeury mint for current mint information /// Qeury mint for current mint information
#[instrument(skip(self))] #[instrument(skip(self))]
pub async fn get_mint_info(&self) -> Result<Option<MintInfo>, Error> { pub async fn get_mint_info(&self) -> Result<Option<MintInfo>, Error> {
let mint_info = match self let mint_info = match self.client.get_mint_info(self.mint_url.clone()).await {
.client
.get_mint_info(self.mint_url.clone().try_into()?)
.await
{
Ok(mint_info) => Some(mint_info), Ok(mint_info) => Some(mint_info),
Err(err) => { Err(err) => {
tracing::warn!("Could not get mint info {}", err); tracing::warn!("Could not get mint info {}", err);
@@ -281,7 +277,7 @@ impl Wallet {
let response = self let response = self
.client .client
.post_restore(self.mint_url.clone().try_into()?, restore_request) .post_restore(self.mint_url.clone(), restore_request)
.await?; .await?;
if response.signatures.is_empty() { if response.signatures.is_empty() {

View File

@@ -77,7 +77,7 @@ impl Wallet {
let spendable = self let spendable = self
.client .client
.post_check_state(self.mint_url.clone().try_into()?, proof_ys) .post_check_state(self.mint_url.clone(), proof_ys)
.await? .await?
.states; .states;
@@ -98,7 +98,7 @@ impl Wallet {
pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<ProofState>, Error> { pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<ProofState>, Error> {
let spendable = self let spendable = self
.client .client
.post_check_state(self.mint_url.clone().try_into()?, proofs.ys()?) .post_check_state(self.mint_url.clone(), proofs.ys()?)
.await?; .await?;
let spent_ys: Vec<_> = spendable let spent_ys: Vec<_> = spendable
.states .states

View File

@@ -34,10 +34,7 @@ impl Wallet {
.await? .await?
.is_none() .is_none()
{ {
tracing::debug!( tracing::debug!("Mint not in localstore fetching info for: {mint_url}");
"Mint not in localstore fetching info for: {}",
self.mint_url
);
self.get_mint_info().await?; self.get_mint_info().await?;
} }
@@ -134,7 +131,7 @@ impl Wallet {
let swap_response = self let swap_response = self
.client .client
.post_swap(mint_url.clone().try_into()?, pre_swap.swap_request) .post_swap(mint_url.clone(), pre_swap.swap_request)
.await?; .await?;
// Proof to keep // Proof to keep

View File

@@ -42,7 +42,7 @@ impl Wallet {
let swap_response = self let swap_response = self
.client .client
.post_swap(mint_url.clone().try_into()?, pre_swap.swap_request) .post_swap(mint_url.clone(), pre_swap.swap_request)
.await?; .await?;
let active_keyset_id = pre_swap.pre_mint_secrets.keyset_id; let active_keyset_id = pre_swap.pre_mint_secrets.keyset_id;