mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-08 22:55:54 +01:00
crawB Binary Token Serialization (#507)
This commit is contained in:
@@ -75,9 +75,12 @@ pub enum Error {
|
||||
/// Base64 error
|
||||
#[error(transparent)]
|
||||
Base64Error(#[from] bitcoin::base64::DecodeError),
|
||||
/// Ciborium error
|
||||
/// Ciborium deserialization error
|
||||
#[error(transparent)]
|
||||
CiboriumError(#[from] ciborium::de::Error<std::io::Error>),
|
||||
/// Ciborium serialization error
|
||||
#[error(transparent)]
|
||||
CiboriumSerError(#[from] ciborium::ser::Error<std::io::Error>),
|
||||
/// Amount Error
|
||||
#[error(transparent)]
|
||||
Amount(#[from] crate::amount::Error),
|
||||
|
||||
@@ -122,6 +122,14 @@ impl Token {
|
||||
|
||||
v3_token.to_string()
|
||||
}
|
||||
|
||||
/// Serialize the token to raw binary
|
||||
pub fn to_raw_bytes(&self) -> Result<Vec<u8>, Error> {
|
||||
match self {
|
||||
Self::TokenV3(_) => Err(Error::UnsupportedToken),
|
||||
Self::TokenV4(token) => token.to_raw_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Token {
|
||||
@@ -152,6 +160,26 @@ impl FromStr for Token {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Vec<u8>> for Token {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 5 {
|
||||
return Err(Error::UnsupportedToken);
|
||||
}
|
||||
|
||||
let prefix = String::from_utf8(bytes[..5].to_vec())?;
|
||||
|
||||
match prefix.as_str() {
|
||||
"crawB" => {
|
||||
let token: TokenV4 = ciborium::from_reader(&bytes[5..])?;
|
||||
Ok(Token::TokenV4(token))
|
||||
}
|
||||
_ => Err(Error::UnsupportedToken),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Token V3 Token
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct TokenV3Token {
|
||||
@@ -329,6 +357,15 @@ impl TokenV4 {
|
||||
pub fn unit(&self) -> &CurrencyUnit {
|
||||
&self.unit
|
||||
}
|
||||
|
||||
/// Serialize the token to raw binary
|
||||
pub fn to_raw_bytes(&self) -> Result<Vec<u8>, Error> {
|
||||
let mut prefix = b"crawB".to_vec();
|
||||
let mut data = Vec::new();
|
||||
ciborium::into_writer(self, &mut data).map_err(Error::CiboriumSerError)?;
|
||||
prefix.extend(data);
|
||||
Ok(prefix)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TokenV4 {
|
||||
@@ -355,6 +392,25 @@ impl FromStr for TokenV4 {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&Vec<u8>> for TokenV4 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 5 {
|
||||
return Err(Error::UnsupportedToken);
|
||||
}
|
||||
|
||||
let prefix = String::from_utf8(bytes[..5].to_vec())?;
|
||||
|
||||
if prefix.as_str() == "crawB" {
|
||||
let token: TokenV4 = ciborium::from_reader(&bytes[5..])?;
|
||||
Ok(token)
|
||||
} else {
|
||||
Err(Error::UnsupportedToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<TokenV3> for TokenV4 {
|
||||
type Error = Error;
|
||||
fn try_from(token: TokenV3) -> Result<Self, Self::Error> {
|
||||
@@ -434,6 +490,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::mint_url::MintUrl;
|
||||
use crate::util::hex;
|
||||
|
||||
#[test]
|
||||
fn test_token_padding() {
|
||||
@@ -554,4 +611,23 @@ mod tests {
|
||||
|
||||
assert!(correct_token.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_v4_raw_roundtrip() {
|
||||
let token_raw = hex::decode("6372617742a4617481a261694800ad268c4d1f5826617081a3616101617378403961366462623834376264323332626137366462306466313937323136623239643362386363313435353363643237383237666331636339343266656462346561635821038618543ffb6b8695df4ad4babcde92a34a96bdcd97dcee0d7ccf98d4721267926164695468616e6b20796f75616d75687474703a2f2f6c6f63616c686f73743a33333338617563736174").unwrap();
|
||||
let token = TokenV4::try_from(&token_raw).expect("Token deserialization error");
|
||||
let token_raw_ = token.to_raw_bytes().expect("Token serialization error");
|
||||
let token_ = TokenV4::try_from(&token_raw_).expect("Token deserialization error");
|
||||
assert!(token_ == token)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_token_generic_raw_roundtrip() {
|
||||
let tokenv4_raw = hex::decode("6372617742a4617481a261694800ad268c4d1f5826617081a3616101617378403961366462623834376264323332626137366462306466313937323136623239643362386363313435353363643237383237666331636339343266656462346561635821038618543ffb6b8695df4ad4babcde92a34a96bdcd97dcee0d7ccf98d4721267926164695468616e6b20796f75616d75687474703a2f2f6c6f63616c686f73743a33333338617563736174").unwrap();
|
||||
let tokenv4 = Token::try_from(&tokenv4_raw).expect("Token deserialization error");
|
||||
let tokenv4_ = TokenV4::try_from(&tokenv4_raw).expect("Token deserialization error");
|
||||
let tokenv4_bytes = tokenv4.to_raw_bytes().expect("Serialization error");
|
||||
let tokenv4_bytes_ = tokenv4_.to_raw_bytes().expect("Serialization error");
|
||||
assert!(tokenv4_bytes_ == tokenv4_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,4 +213,47 @@ impl Wallet {
|
||||
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
/// Receive
|
||||
/// # Synopsis
|
||||
/// ```rust, no_run
|
||||
/// use std::sync::Arc;
|
||||
///
|
||||
/// use cdk::amount::SplitTarget;
|
||||
/// use cdk::cdk_database::WalletMemoryDatabase;
|
||||
/// use cdk::nuts::CurrencyUnit;
|
||||
/// use cdk::wallet::Wallet;
|
||||
/// use cdk::util::hex;
|
||||
/// use rand::Rng;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> anyhow::Result<()> {
|
||||
/// let seed = rand::thread_rng().gen::<[u8; 32]>();
|
||||
/// let mint_url = "https://testnut.cashu.space";
|
||||
/// let unit = CurrencyUnit::Sat;
|
||||
///
|
||||
/// let localstore = WalletMemoryDatabase::default();
|
||||
/// let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None).unwrap();
|
||||
/// let token_raw = hex::decode("6372617742a4617481a261694800ad268c4d1f5826617081a3616101617378403961366462623834376264323332626137366462306466313937323136623239643362386363313435353363643237383237666331636339343266656462346561635821038618543ffb6b8695df4ad4babcde92a34a96bdcd97dcee0d7ccf98d4721267926164695468616e6b20796f75616d75687474703a2f2f6c6f63616c686f73743a33333338617563736174").unwrap();
|
||||
/// let amount_receive = wallet.receive_raw(&token_raw, SplitTarget::default(), &[], &[]).await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[instrument(skip_all)]
|
||||
pub async fn receive_raw(
|
||||
&self,
|
||||
binary_token: &Vec<u8>,
|
||||
amount_split_target: SplitTarget,
|
||||
p2pk_signing_keys: &[SecretKey],
|
||||
preimages: &[String],
|
||||
) -> Result<Amount, Error> {
|
||||
let token_str = Token::try_from(binary_token)?.to_string();
|
||||
self.receive(
|
||||
token_str.as_str(),
|
||||
amount_split_target,
|
||||
p2pk_signing_keys,
|
||||
preimages,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user