diff --git a/Cargo.lock b/Cargo.lock index 5faa66f..4b9405a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1164,18 +1164,28 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1184,14 +1194,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -1441,6 +1452,7 @@ dependencies = [ "prost-build", "prost-types", "serde", + "serde_json", "thiserror", "thiserror-core", "ur", diff --git a/libs/ur-registry/Cargo.toml b/libs/ur-registry/Cargo.toml index 1ac88d9..8effba8 100644 --- a/libs/ur-registry/Cargo.toml +++ b/libs/ur-registry/Cargo.toml @@ -38,3 +38,4 @@ prost-build = { version = "0.11.8" } [dev-dependencies] base64 = { version = "0.22.1" } +serde_json = { version = "1.0.95", default-features = false, features = ["alloc"] } diff --git a/libs/ur-registry/src/lib.rs b/libs/ur-registry/src/lib.rs index fc4f171..7a2f84d 100644 --- a/libs/ur-registry/src/lib.rs +++ b/libs/ur-registry/src/lib.rs @@ -40,3 +40,4 @@ pub mod ton; pub mod traits; mod types; pub mod zcash; +pub mod xrp; \ No newline at end of file diff --git a/libs/ur-registry/src/macros_impl.rs b/libs/ur-registry/src/macros_impl.rs index 16011e0..8207a89 100644 --- a/libs/ur-registry/src/macros_impl.rs +++ b/libs/ur-registry/src/macros_impl.rs @@ -55,6 +55,7 @@ use crate::stellar::{ use crate::sui::sui_signature::SuiSignature; use crate::sui::{sui_sign_hash_request::SuiSignHashRequest, sui_sign_request::SuiSignRequest}; use crate::ton::{ton_sign_request::TonSignRequest, ton_signature::TonSignature}; +use crate::xrp::{xrp_batch_sign_request::XrpBatchSignRequest, xrp_batch_signature::XrpBatchSignature}; use crate::zcash::zcash_accounts::ZcashAccounts; use crate::zcash::zcash_pczt::ZcashPczt; use crate::zcash::zcash_unified_full_viewing_key::ZcashUnifiedFullViewingKey; @@ -133,4 +134,6 @@ impl_cbor_bytes!( IotaSignRequest, IotaSignHashRequest, IotaSignature, + XrpBatchSignRequest, + XrpBatchSignature, ); diff --git a/libs/ur-registry/src/registry_types.rs b/libs/ur-registry/src/registry_types.rs index 908b380..704fee3 100644 --- a/libs/ur-registry/src/registry_types.rs +++ b/libs/ur-registry/src/registry_types.rs @@ -36,6 +36,8 @@ pub enum URType { IotaSignHashRequest(String), ErgoSignRequest(String), SolSignature(String), + XrpBatchSignRequest(String), + XrpBatchSignature(String), } impl URType { @@ -80,6 +82,7 @@ impl URType { "iota-sign-request" => Ok(URType::IotaSignRequest(type_str.to_string())), "ergo-sign-request" => Ok(URType::ErgoSignRequest(type_str.to_string())), "sol-signature" => Ok(URType::SolSignature(type_str.to_string())), + "xrp-batch-sign-request" => Ok(URType::XrpBatchSignRequest(type_str.to_string())), _ => Err(URError::NotSupportURTypeError(type_str.to_string())), } } @@ -119,6 +122,8 @@ impl URType { URType::IotaSignHashRequest(type_str) => type_str.to_string(), URType::ErgoSignRequest(type_str) => type_str.to_string(), URType::SolSignature(type_str) => type_str.to_string(), + URType::XrpBatchSignRequest(type_str) => type_str.to_string(), + URType::XrpBatchSignature(type_str) => type_str.to_string(), } } } @@ -237,6 +242,10 @@ pub const IOTA_SIGN_REQUEST: RegistryType = RegistryType("iota-sign-request", So pub const IOTA_SIGNATURE: RegistryType = RegistryType("iota-signature", Some(8502)); pub const IOTA_SIGN_HASH_REQUEST: RegistryType = RegistryType("iota-sign-hash-request", Some(8503)); +// XRP +pub const XRP_BATCH_SIGN_REQUEST: RegistryType = RegistryType("xrp-batch-sign-request", Some(48601)); +pub const XRP_BATCH_SIGNATURE: RegistryType = RegistryType("xrp-batch-signature", Some(48602)); + // Zcash pub const ZCASH_ACCOUNTS: RegistryType = RegistryType("zcash-accounts", Some(49201)); pub const ZCASH_FULL_VIEWING_KEY: RegistryType = diff --git a/libs/ur-registry/src/xrp/mod.rs b/libs/ur-registry/src/xrp/mod.rs new file mode 100644 index 0000000..99c5cdd --- /dev/null +++ b/libs/ur-registry/src/xrp/mod.rs @@ -0,0 +1,2 @@ +pub mod xrp_batch_sign_request; +pub mod xrp_batch_signature; diff --git a/libs/ur-registry/src/xrp/xrp_batch_sign_request.rs b/libs/ur-registry/src/xrp/xrp_batch_sign_request.rs new file mode 100644 index 0000000..844517c --- /dev/null +++ b/libs/ur-registry/src/xrp/xrp_batch_sign_request.rs @@ -0,0 +1,213 @@ +use crate::cbor::cbor_map; +use crate::error::{URError, URResult}; +use crate::registry_types::{RegistryType, UUID, XRP_BATCH_SIGN_REQUEST}; +use crate::traits::{From as FromCbor, RegistryItem, To}; +use crate::types::Bytes; +use alloc::string::ToString; +use alloc::vec::Vec; +use minicbor::data::{Int, Tag}; +use minicbor::encode::Write; +use minicbor::{Decoder, Encoder}; + +const SIGN_DATA: u8 = 1; + +#[derive(Clone, Debug, Default)] +pub struct XrpBatchSignRequest { + sign_data: Vec, +} + +impl XrpBatchSignRequest { + pub fn default() -> Self { + Default::default() + } + + pub fn set_sign_data(&mut self, data: Vec) { + self.sign_data = data; + } + + pub fn new(sign_data: Vec) -> XrpBatchSignRequest { + XrpBatchSignRequest { + sign_data, + } + } + pub fn get_sign_data(&self) -> Vec { + self.sign_data.clone() + } + + fn get_map_size(&self) -> u64 { + 1 + } +} + +impl RegistryItem for XrpBatchSignRequest { + fn get_registry_type() -> RegistryType<'static> { + XRP_BATCH_SIGN_REQUEST + } +} + +impl minicbor::Encode for XrpBatchSignRequest { + fn encode( + &self, + e: &mut Encoder, + _ctx: &mut C, + ) -> Result<(), minicbor::encode::Error> { + e.map(self.get_map_size())?; + + e.int(Int::from(SIGN_DATA))?; + let sign_data_len = self.sign_data.len().try_into().unwrap(); + e.array(sign_data_len)?; + for ele in &self.sign_data { + e.bytes(ele)?; + } + + e.int(Int::from(SIGN_DATA))?; + let sign_data_len = self.sign_data.len().try_into().unwrap(); + e.array(sign_data_len)?; + for ele in &self.sign_data { + e.bytes(ele)?; + } + + Ok(()) + } +} + +impl<'b, C> minicbor::Decode<'b, C> for XrpBatchSignRequest { + fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result { + let mut result = XrpBatchSignRequest::default(); + cbor_map(d, &mut result, |key, obj, d| { + let key = + u8::try_from(key).map_err(|e| minicbor::decode::Error::message(e.to_string()))?; + match key { + SIGN_DATA => { + let sign_data_len = d.array()?; + obj.sign_data = Vec::new(); + if sign_data_len.is_some() { + for _ in 0..sign_data_len.unwrap() { + obj.sign_data.push(d.bytes()?.to_vec()); + } + } + } + _ => {} + } + Ok(()) + })?; + Ok(result) + } +} + +impl To for XrpBatchSignRequest { + fn to_bytes(&self) -> URResult> { + minicbor::to_vec(self.clone()).map_err(|e| URError::CborEncodeError(e.to_string())) + } +} + +impl FromCbor for XrpBatchSignRequest { + fn from_cbor(bytes: Vec) -> URResult { + minicbor::decode(&bytes).map_err(|e| URError::CborDecodeError(e.to_string())) + } +} + +#[cfg(test)] +mod tests { + use crate::traits::{From as FromCbor, To}; + use crate::xrp::xrp_batch_sign_request::XrpBatchSignRequest; + use alloc::vec::Vec; + use hex::FromHex; + extern crate std; + use std::println; + + #[test] + fn test_xrp_batch_sign_request_encode() { + let sign_data_str = [ + r#" + { + "TransactionType": "Payment", + "Amount": "101", + "Destination": "rDur9gS6DjqrwGnPRa4RZeiPpkqtv2Gf2m", + "DestinationTag": 987654321, + "Flags": 2147483648, + "Account": "rDur9gS6DjqrwGnPRa4RZeiPpkqtv2Gf2m", + "Fee": "12", + "Sequence": 82376319, + "LastLedgerSequence": 83749165, + "SigningPubKey": "03B91E16E98BA86B62A52AAA2D41C114B36C8BFCD862B1ECED77DC5D77676510F8" + } + "#, + r#" + { + "TransactionType": "Payment", + "Amount": "1001", + "Destination": "rDur9gS6DjqrwGnPRa4RZeiPpkqtv2Gf2m", + "DestinationTag": 123456, + "Flags": 22222, + "Account": "rDur9gS6DjqrwGnPRa4RZeiPpkqtv2Gf2m", + "Fee": "12", + "Sequence": 123456, + "LastLedgerSequence": 123456, + "SigningPubKey": "03B91E16E98BA86B62A52AAA2D41C114B36C8BFCD862B1ECED77DC5D77676510F8" + } + "#, + ]; + let sign_data: Vec> = sign_data_str + .iter() + .map(|s| { + let json_value: serde_json::Value = + serde_json::from_str(s.trim()).expect("Invalid JSON format"); + serde_json::to_vec(&json_value).expect("Failed to serialize JSON to bytes") + }) + .collect(); + let request = XrpBatchSignRequest::new(sign_data); + assert_eq!(hex::encode(request.to_bytes().unwrap()), + "a1018259014e7b224163636f756e74223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c22416d6f756e74223a22313031222c2244657374696e6174696f6e223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c2244657374696e6174696f6e546167223a3938373635343332312c22466565223a223132222c22466c616773223a323134373438333634382c224c6173744c656467657253657175656e6365223a38333734393136352c2253657175656e6365223a38323337363331392c225369676e696e675075624b6579223a22303342393145313645393842413836423632413532414141324434314331313442333643384246434438363242314543454437374443354437373637363531304638222c225472616e73616374696f6e54797065223a225061796d656e74227d5901437b224163636f756e74223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c22416d6f756e74223a2231303031222c2244657374696e6174696f6e223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c2244657374696e6174696f6e546167223a3132333435362c22466565223a223132222c22466c616773223a32323232322c224c6173744c656467657253657175656e6365223a3132333435362c2253657175656e6365223a3132333435362c225369676e696e675075624b6579223a22303342393145313645393842413836423632413532414141324434314331313442333643384246434438363242314543454437374443354437373637363531304638222c225472616e73616374696f6e54797065223a225061796d656e74227d018259014e7b224163636f756e74223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c22416d6f756e74223a22313031222c2244657374696e6174696f6e223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c2244657374696e6174696f6e546167223a3938373635343332312c22466565223a223132222c22466c616773223a323134373438333634382c224c6173744c656467657253657175656e6365223a38333734393136352c2253657175656e6365223a38323337363331392c225369676e696e675075624b6579223a22303342393145313645393842413836423632413532414141324434314331313442333643384246434438363242314543454437374443354437373637363531304638222c225472616e73616374696f6e54797065223a225061796d656e74227d5901437b224163636f756e74223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c22416d6f756e74223a2231303031222c2244657374696e6174696f6e223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c2244657374696e6174696f6e546167223a3132333435362c22466565223a223132222c22466c616773223a32323232322c224c6173744c656467657253657175656e6365223a3132333435362c2253657175656e6365223a3132333435362c225369676e696e675075624b6579223a22303342393145313645393842413836423632413532414141324434314331313442333643384246434438363242314543454437374443354437373637363531304638222c225472616e73616374696f6e54797065223a225061796d656e74227d" + ); + } + + #[test] + fn test_xrp_batch_sign_request_decode() { + let sign_data: Vec = hex::decode("a301d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d028259014e7b224163636f756e74223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c22416d6f756e74223a22313031222c2244657374696e6174696f6e223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c2244657374696e6174696f6e546167223a3938373635343332312c22466565223a223132222c22466c616773223a323134373438333634382c224c6173744c656467657253657175656e6365223a38333734393136352c2253657175656e6365223a38323337363331392c225369676e696e675075624b6579223a22303342393145313645393842413836423632413532414141324434314331313442333643384246434438363242314543454437374443354437373637363531304638222c225472616e73616374696f6e54797065223a225061796d656e74227d5901437b224163636f756e74223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c22416d6f756e74223a2231303031222c2244657374696e6174696f6e223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c2244657374696e6174696f6e546167223a3132333435362c22466565223a223132222c22466c616773223a32323232322c224c6173744c656467657253657175656e6365223a3132333435362c2253657175656e6365223a3132333435362c225369676e696e675075624b6579223a22303342393145313645393842413836423632413532414141324434314331313442333643384246434438363242314543454437374443354437373637363531304638222c225472616e73616374696f6e54797065223a225061796d656e74227d028259014e7b224163636f756e74223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c22416d6f756e74223a22313031222c2244657374696e6174696f6e223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c2244657374696e6174696f6e546167223a3938373635343332312c22466565223a223132222c22466c616773223a323134373438333634382c224c6173744c656467657253657175656e6365223a38333734393136352c2253657175656e6365223a38323337363331392c225369676e696e675075624b6579223a22303342393145313645393842413836423632413532414141324434314331313442333643384246434438363242314543454437374443354437373637363531304638222c225472616e73616374696f6e54797065223a225061796d656e74227d5901437b224163636f756e74223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c22416d6f756e74223a2231303031222c2244657374696e6174696f6e223a227244757239675336446a717277476e50526134525a656950706b717476324766326d222c2244657374696e6174696f6e546167223a3132333435362c22466565223a223132222c22466c616773223a32323232322c224c6173744c656467657253657175656e6365223a3132333435362c2253657175656e6365223a3132333435362c225369676e696e675075624b6579223a22303342393145313645393842413836423632413532414141324434314331313442333643384246434438363242314543454437374443354437373637363531304638222c225472616e73616374696f6e54797065223a225061796d656e74227d").unwrap(); + let xrp_batch_sign_request = XrpBatchSignRequest::from_cbor(sign_data).unwrap(); + let first_sign_data = xrp_batch_sign_request.get_sign_data()[0].clone(); + let json_value: serde_json::Value = serde_json::from_slice(&first_sign_data).unwrap(); + assert_eq!( + json_value.get("TransactionType").unwrap().as_str().unwrap(), + "Payment" + ); + assert_eq!( + json_value.get("Amount").unwrap().as_str().unwrap(), + "101" + ); + assert_eq!( + json_value.get("Destination").unwrap().as_str().unwrap(), + "rDur9gS6DjqrwGnPRa4RZeiPpkqtv2Gf2m" + ); + assert_eq!( + json_value.get("DestinationTag").unwrap().as_u64().unwrap(), + 987654321 + ); + assert_eq!( + json_value.get("Flags").unwrap().as_u64().unwrap(), + 2147483648 + ); + assert_eq!( + json_value.get("Account").unwrap().as_str().unwrap(), + "rDur9gS6DjqrwGnPRa4RZeiPpkqtv2Gf2m" + ); + assert_eq!(json_value.get("Fee").unwrap().as_str().unwrap(), "12"); + assert_eq!( + json_value.get("Sequence").unwrap().as_u64().unwrap(), + 82376319 + ); + assert_eq!( + json_value + .get("LastLedgerSequence") + .unwrap() + .as_u64() + .unwrap(), + 83749165 + ); + assert_eq!( + json_value.get("SigningPubKey").unwrap().as_str().unwrap(), + "03B91E16E98BA86B62A52AAA2D41C114B36C8BFCD862B1ECED77DC5D77676510F8" + ); + } +} diff --git a/libs/ur-registry/src/xrp/xrp_batch_signature.rs b/libs/ur-registry/src/xrp/xrp_batch_signature.rs new file mode 100644 index 0000000..280c780 --- /dev/null +++ b/libs/ur-registry/src/xrp/xrp_batch_signature.rs @@ -0,0 +1,97 @@ +use crate::cbor::cbor_map; +use crate::error::{URError, URResult}; +use crate::registry_types::{RegistryType, XRP_BATCH_SIGNATURE, UUID}; +use crate::traits::{From as FromCbor, RegistryItem, To}; +use crate::types::Bytes; +use alloc::string::ToString; +use alloc::vec::Vec; +use minicbor::data::{Int, Tag}; +use minicbor::encode::Write; +use minicbor::{Decoder, Encoder}; + +const SIGNATURE: u8 = 1; + +#[derive(Clone, Debug, Default)] +pub struct XrpBatchSignature { + signatures: Vec, +} + +impl XrpBatchSignature { + pub fn default() -> Self { + Default::default() + } + + pub fn set_signature(&mut self, signature: Vec) { + self.signatures = signature; + } + + pub fn new(signatures: Vec) -> Self { + XrpBatchSignature { + signatures, + } + } + + pub fn get_signature(&self) -> Vec { + self.signatures.clone() + } +} + +impl RegistryItem for XrpBatchSignature { + fn get_registry_type() -> RegistryType<'static> { + XRP_BATCH_SIGNATURE + } +} + +impl minicbor::Encode for XrpBatchSignature { + fn encode( + &self, + e: &mut Encoder, + _ctx: &mut C, + ) -> Result<(), minicbor::encode::Error> { + let mut size = 1; + e.map(size)?; + e.int(Int::from(SIGNATURE))?; + let len = self.signatures.len().try_into().unwrap(); + e.array(len)?; + for ele in &self.signatures { + e.bytes(ele)?; + } + Ok(()) + } +} + +impl<'b, C> minicbor::Decode<'b, C> for XrpBatchSignature { + fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result { + let mut result = XrpBatchSignature::default(); + cbor_map(d, &mut result, |key, obj, d| { + let key = + u8::try_from(key).map_err(|e| minicbor::decode::Error::message(e.to_string()))?; + match key { + SIGNATURE => { + let len = d.array()?; + obj.signatures = Vec::new(); + if len.is_some() { + for _ in 0..len.unwrap() { + obj.signatures.push(d.bytes()?.to_vec()); + } + } + } + _ => {} + } + Ok(()) + })?; + Ok(result) + } +} + +impl To for XrpBatchSignature { + fn to_bytes(&self) -> URResult> { + minicbor::to_vec(self.clone()).map_err(|e| URError::CborEncodeError(e.to_string())) + } +} + +impl FromCbor for XrpBatchSignature { + fn from_cbor(bytes: Vec) -> URResult { + minicbor::decode(&bytes).map_err(|e| URError::CborDecodeError(e.to_string())) + } +}