diff --git a/libs/ur-registry/src/avalanche/avax_sign_request.rs b/libs/ur-registry/src/avalanche/avax_sign_request.rs index c0dcb7c..f24edf2 100644 --- a/libs/ur-registry/src/avalanche/avax_sign_request.rs +++ b/libs/ur-registry/src/avalanche/avax_sign_request.rs @@ -1,8 +1,10 @@ use crate::cbor::{cbor_array, cbor_map}; use crate::error::{URError, URResult}; -use crate::registry_types::{RegistryType, AVAX_SIGN_REQUEST, UUID}; +use crate::registry_types::{RegistryType, AVAX_SIGN_REQUEST, UUID, CRYPTO_KEYPATH, AVAX_UTXO}; +use crate::crypto_key_path::CryptoKeyPath; use crate::traits::{From as FromCbor, RegistryItem, To}; use crate::types::{Bytes, Fingerprint}; +use super::avax_utxo::AvaxUtxo; use alloc::string::{String, ToString}; use alloc::vec::Vec; use minicbor::data::{Int, Tag}; @@ -11,33 +13,29 @@ use minicbor::{Decoder, Encoder}; const REQUEST_ID: u8 = 1; const SIGN_DATA: u8 = 2; -const MASTER_FINGERPRINT: u8 = 3; -const XPUB: u8 = 6; -const WALLET_INDEX: u8 = 7; +const DERIVATION_PATH: u8 = 3; +const UTXOS: u8 = 4; #[derive(Debug, Clone, Default)] pub struct AvaxSignRequest { request_id: Bytes, sign_data: Bytes, - master_fingerprint: Fingerprint, - xpub: String, - wallet_index: u64, + derivation_path: Vec, + utxos: Vec, } impl AvaxSignRequest { pub fn new( request_id: Bytes, sign_data: Bytes, - master_fingerprint: Fingerprint, - xpub: String, - wallet_index: u64, + derivation_path: Vec, + utxos: Vec, ) -> Self { AvaxSignRequest { request_id, sign_data, - master_fingerprint, - xpub, - wallet_index, + derivation_path, + utxos: utxos, } } @@ -57,24 +55,20 @@ impl AvaxSignRequest { self.sign_data = data; } - pub fn get_master_fingerprint(&self) -> Fingerprint { - self.master_fingerprint + pub fn get_derivation_path(&self) -> Vec { + self.derivation_path.clone() } - pub fn get_xpub(&self) -> String { - self.xpub.clone() + pub fn set_derivation_path(&mut self, derivation_path: Vec) { + self.derivation_path = derivation_path; } - pub fn set_xpub(&mut self, xpub: String) { - self.xpub = xpub; + pub fn get_utxos(&self) -> Vec { + self.utxos.clone() } - pub fn get_wallet_index(&self) -> u64 { - self.wallet_index - } - - pub fn set_wallet_index(&mut self, index: u64) { - self.wallet_index = index; + pub fn set_utxos(&mut self, utxos: Vec) { + self.utxos = utxos; } } @@ -90,23 +84,43 @@ impl minicbor::Encode for AvaxSignRequest { e: &mut Encoder, _ctx: &mut C, ) -> Result<(), minicbor::encode::Error> { - e.map(5)?; + let utxos = self.get_utxos(); + let map_size = if utxos.is_empty() { 3 } else { 4 }; + + e.map(map_size)?; e.int(Int::from(REQUEST_ID))? .tag(Tag::Unassigned(UUID.get_tag()))? .bytes(&self.request_id)?; e.int(Int::from(SIGN_DATA))?.bytes(&self.sign_data)?; - e.int(Int::from(MASTER_FINGERPRINT))?.int( - Int::try_from(u32::from_be_bytes(self.master_fingerprint)) - .map_err(|e| minicbor::encode::Error::message(e.to_string()))?, - )?; - e.int(Int::from(XPUB))?.str(&self.xpub)?; - e.int(Int::from(WALLET_INDEX))?.u64(self.wallet_index)?; + + let key_derivation_paths = self.get_derivation_path(); + if key_derivation_paths.is_empty() { + return Err(minicbor::encode::Error::message( + "key derivation paths is invalid", + )); + } + + e.int(Int::from(DERIVATION_PATH))?.array(key_derivation_paths.len() as u64)?; + for path in key_derivation_paths { + e.tag(Tag::Unassigned(CRYPTO_KEYPATH.get_tag()))?; + CryptoKeyPath::encode(&path, e, _ctx)?; + } + + if !utxos.is_empty() { + e.int(Int::from(UTXOS))? + .array(utxos.len() as u64)?; + for utxo in utxos { + e.tag(Tag::Unassigned(AvaxUtxo::get_registry_type().get_tag()))?; + AvaxUtxo::encode(&utxo, e, _ctx)?; + } + } + Ok(()) } } impl<'b, C> minicbor::Decode<'b, C> for AvaxSignRequest { - fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result { + fn decode(d: &mut Decoder<'b>, ctx: &mut C) -> Result { let mut result = AvaxSignRequest::default(); cbor_map(d, &mut result, |key, obj, d| { @@ -120,16 +134,37 @@ impl<'b, C> minicbor::Decode<'b, C> for AvaxSignRequest { SIGN_DATA => { obj.sign_data = d.bytes()?.to_vec(); } - MASTER_FINGERPRINT => { - let mfp = u32::try_from(d.int()?) - .map_err(|e| minicbor::decode::Error::message(e.to_string())); - obj.master_fingerprint = u32::to_be_bytes(mfp?); - } - XPUB => { - obj.xpub = d.str()?.to_string(); + DERIVATION_PATH => { + cbor_array( + d, + &mut obj.derivation_path, + |_key, obj, d| { + let tag = d.tag()?; + if !tag.eq(&Tag::Unassigned( + CryptoKeyPath::get_registry_type().get_tag(), + )) { + return Err(minicbor::decode::Error::message( + "CryptoKeyPath tag is invalid", + )); + } + obj.push(CryptoKeyPath::decode(d, ctx)?); + Ok(()) + }, + )?; } - WALLET_INDEX => { - obj.wallet_index = d.u64()?; + UTXOS => { + cbor_array(d, &mut obj.utxos, |_key, obj, d| { + let tag = d.tag()?; + if !tag.eq(&Tag::Unassigned( + AvaxUtxo::get_registry_type().get_tag(), + )) { + return Err(minicbor::decode::Error::message( + "AvaxUtxo tag is invalid", + )); + } + obj.push(AvaxUtxo::decode(d, ctx)?); + Ok(()) + })?; } _ => {} } @@ -157,35 +192,49 @@ mod tests { use crate::traits::RegistryItem; use alloc::vec::Vec; use hex::FromHex; + use crate::crypto_key_path::{CryptoKeyPath, PathComponent}; + use alloc::vec; extern crate std; use std::println; #[test] fn test_avax_encode() { + let components = vec![ + PathComponent::new(Some(44), true).unwrap(), + PathComponent::new(Some(9000), true).unwrap(), + PathComponent::new(Some(0), true).unwrap(), + PathComponent::new(Some(0), false).unwrap(), + PathComponent::new(Some(0), false).unwrap(), + ]; + let utxos = vec![]; let unsigned_data = AvaxSignRequest { request_id: [12, 34, 56, 78].to_vec(), - sign_data: Vec::from_hex("000000000022000000050000000000000000000000000000000000000000000000000000000000000000000000023d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa0000000700000000000f42400000000000000000000000010000000132336f8715dd313a426155cccc15ba27c3033dae3d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000007000000004d58ade90000000000000000000000010000000132336f8715dd313a426155cccc15ba27c3033dae00000001410b47f7c7aa13f88122be58735c5e985edc65d86fb0baf0b016359c22253d75000000013d9bdac0ed1d761330cf680efdeb1a42159eb387d6d2950c96f7d28f61bbe2aa00000005000000004d680464000000010000000000000000") + sign_data: Vec::from_hex("0000000000220000000100000000000000000000000000000000000000000000000000000000000000000000000221e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000070000000000007d4c00000000000000000000000100000001b5e66be5c7093d1114d74940333c0c45f81092c521e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000070000000002ed658f00000000000000000000000100000001b5e66be5c7093d1114d74940333c0c45f81092c500000001918cf421e834d4d7031175ac9605ba292ee04a17beb4fb81f8557969b4651b860000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000050000000002edf716000000010000000000000000") .unwrap(), - master_fingerprint: [0, 0, 0, 0], - xpub: "xpub6DXryz8Kd7XchtXvDnkjara83shGJH8ubu7KZhHhPfp4L1shvDEYiFZm32EKHnyo4bva4gxXjabFGqY7fNs8Ggd4khYz2oNs2KYLf56a9GX".to_string(), - wallet_index: 0, + derivation_path: vec![CryptoKeyPath::new( + components, + Some([45,11,218,188]), + None, + )], + utxos }; let result: Vec = unsigned_data.try_into().unwrap(); println!("result = {:?}", hex::encode(&result)); let ur = ur::encode(&result, AvaxSignRequest::get_registry_type().get_type()); - assert_eq!(ur, "ur:avax-sign-request/onadtpdafybncpetglaohkaddmaeaeaeaeaecpaeaeaeahaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaofsndtnrtwecakobwdytkisbazcwmcyfwbznnqdlttbtdmdbnmtyltdmyhsrkvopkaeaeaeataeaeaeaeaebsfwfzaeaeaeaeaeaeaeaeaeaeaeadaeaeaeadeyeojlltbzutehftfwhsgosfsfbzrddisraxfsplfsndtnrtwecakobwdytkisbazcwmcyfwbznnqdlttbtdmdbnmtyltdmyhsrkvopkaeaeaeataeaeaeaegthdpmwlaeaeaeaeaeaeaeaeaeaeaeadaeaeaeadeyeojlltbzutehftfwhsgosfsfbzrddisraxfsplaeaeaeadfpbdflylstpkbwyalycprnhdjkhhhymkhyuoihtpjlpfrdwtpfcmecnscpdafskpaeaeaeadfsndtnrtwecakobwdytkisbazcwmcyfwbznnqdlttbtdmdbnmtyltdmyhsrkvopkaeaeaeahaeaeaeaegtisaaieaeaeaeadaeaeaeaeaeaeaeaeaxaeamksjlksjokpidenfyhdjpkkknetgrieemhdiaisjyhdkofyjtjeimhsjphseteojkisflgefdetkpidkpemgrhtisfdisgdiyjoeegsehjkiskofyfehkinfghtjneoeyfegrfdjtkkjleeidkohseeiokshdimhsidfgfljshkemiygljketflioieeejeishkkneyjlgljkeygrhkgsiyecenhsesflhdataedapawecf"); + assert_eq!(ur, "ur:avax-sign-request/otadtpdafybncpetglaohkaddmaeaeaeaeaecpaeaeaeadaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaoclvajkchsbssrndrwmaeiokntbfgdikspdykcpjyrhtbahurdameprdydipdkizmaeaeaeataeaeaeaeaeaekigsaeaeaeaeaeaeaeaeaeaeaeadaeaeaeadrevajevwstasfsbybbtsgafzeofnbnfeyabemoskclvajkchsbssrndrwmaeiokntbfgdikspdykcpjyrhtbahurdameprdydipdkizmaeaeaeataeaeaeaeaoweihmyaeaeaeaeaeaeaeaeaeaeaeadaeaeaeadrevajevwstasfsbybbtsgafzeofnbnfeyabemoskaeaeaeadmelkwkclvseetytsaxbykppsmtahrddtdmvtgechrnqzzolyyagokkinqzihcwlnaeaeaeadclvajkchsbssrndrwmaeiokntbfgdikspdykcpjyrhtbahurdameprdydipdkizmaeaeaeahaeaeaeaeaoweylcmaeaeaeadaeaeaeaeaeaeaeaeaxlytaaddyoeadlecsdwykcfcndeykaeykaewkaewkaocydpbdtnrfcxutdyjn"); } #[test] fn test_avax_decode() { + let ur_string = "ur:avax-sign-request/otadtpdafybncpetglaohkaddmaeaeaeaeaecpaeaeaeahaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaofsndtnrtwecakobwdytkisbazcwmcyfwbznnqdlttbtdmdbnmtyltdmyhsrkvopkaeaeaeataeaeaeaeaebsfwfzaeaeaeaeaeaeaeaeaeaeaeadaeaeaeadeyeojlltbzutehftfwhsgosfsfbzrddisraxfsplfsndtnrtwecakobwdytkisbazcwmcyfwbznnqdlttbtdmdbnmtyltdmyhsrkvopkaeaeaeataeaeaeaegthdpmwlaeaeaeaeaeaeaeaeaeaeaeadaeaeaeadeyeojlltbzutehftfwhsgosfsfbzrddisraxfsplaeaeaeadfpbdflylstpkbwyalycprnhdjkhhhymkhyuoihtpjlpfrdwtpfcmecnscpdafskpaeaeaeadfsndtnrtwecakobwdytkisbazcwmcyfwbznnqdlttbtdmdbnmtyltdmyhsrkvopkaeaeaeahaeaeaeaegtisaaieaeaeaeadaeaeaeaeaeaeaeaeaxtaaddyoeadlecsdwykcfcndeykaeykaewkaewkaocybggdrprfknlulrbk"; + let bytes = - Vec::from_hex("a501d825440c22384e0258de00000000000000000001ed5f38341e436e5d46e2bb00b45d62ae97d1b050c64bc634ae10626739e35c4b0000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000007000000000089544000000000000000000000000100000001512e7191685398f00663e12197a3d8f6012d9ea300000001db720ad6707915cc4751fb7e5491a3af74e127a1d81817abe9438590c0833fe10000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000050000000000989680000000010000000000000000031a0102030406786f7870756236445872797a384b6437586368745876446e6b6a61726138337368474a4838756275374b5a684868506670344c3173687644455969465a6d3332454b486e796f34627661346778586a61624647715937664e7338476764346b68597a326f4e73324b594c663536613947580706") + Vec::from_hex("a401d82550d797b45aef4b483cb106506e288b2c77025903960000000000220000000100000000000000000000000000000000000000000000000000000000000000000000000221e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000007000000000031cb3a00000000000000000000000100000001b5e66be5c7093d1114d74940333c0c45f81092c521e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000070000000005dabac900000000000000000000000100000001b5e66be5c7093d1114d74940333c0c45f81092c500000008120d0def706b8b759935b8ea9727662aafa5381e598a074daddc82492549cd760000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000005000000000046239d0000000100000000174d1a9b28e1d4d518f1999d4f8ac422b8a3a4755001f5965e8d05c93359feb10000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000005000000000131021c0000000100000000174d1a9b28e1d4d518f1999d4f8ac422b8a3a4755001f5965e8d05c93359feb10000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000005000000000176b6a5000000010000000065a3b1de10620296debfa01aa953e45ddd19d2c39e3dacb9a92e6a85ca8a309c0000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000005000000000098968000000001000000006f6522ae52b0231076dc63ff95f7ea22e2fd80943e37235302c7ee32afce4cd60000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000500000000009896800000000100000000845649c3d1a630d8b466f7b727f6577cb4a17864699e6de756e484b81d84cd2a0000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000500000000006c2b440000000100000000d1e6480c1825197e2ec293a60bacdc7f60bfba2f3cc5383855180b45d595a7030000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff0000000500000000007a12000000000100000000f59b9a175ebe4ccd8de5dcfc6a26870414f30c696cce19283f30145624b445b70000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000005000000000107a4930000000100000000000000000382d90130a2018a182cf5192328f500f500f400f4021a2d0bdabcd90130a2018a182cf5192328f500f500f401f4021a2d0bdabc0480") .unwrap(); let data = AvaxSignRequest::try_from(bytes).unwrap(); assert_eq!( data.get_tx_data(), - Vec::from_hex("00000000000000000001ed5f38341e436e5d46e2bb00b45d62ae97d1b050c64bc634ae10626739e35c4b0000000121e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff00000007000000000089544000000000000000000000000100000001512e7191685398f00663e12197a3d8f6012d9ea300000001db720ad6707915cc4751fb7e5491a3af74e127a1d81817abe9438590c0833fe10000000021e67317cbc4be2aeb00677ad6462778a8f52274b9d605df2591b23027a87dff000000050000000000989680000000010000000000000000") - .unwrap() + [0, 0, 0, 0, 0, 34, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 7, 0, 0, 0, 0, 0, 49, 203, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 181, 230, 107, 229, 199, 9, 61, 17, 20, 215, 73, 64, 51, 60, 12, 69, 248, 16, 146, 197, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 7, 0, 0, 0, 0, 5, 218, 186, 201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 181, 230, 107, 229, 199, 9, 61, 17, 20, 215, 73, 64, 51, 60, 12, 69, 248, 16, 146, 197, 0, 0, 0, 8, 18, 13, 13, 239, 112, 107, 139, 117, 153, 53, 184, 234, 151, 39, 102, 42, 175, 165, 56, 30, 89, 138, 7, 77, 173, 220, 130, 73, 37, 73, 205, 118, 0, 0, 0, 0, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 5, 0, 0, 0, 0, 0, 70, 35, 157, 0, 0, 0, 1, 0, 0, 0, 0, 23, 77, 26, 155, 40, 225, 212, 213, 24, 241, 153, 157, 79, 138, 196, 34, 184, 163, 164, 117, 80, 1, 245, 150, 94, 141, 5, 201, 51, 89, 254, 177, 0, 0, 0, 0, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 5, 0, 0, 0, 0, 1, 49, 2, 28, 0, 0, 0, 1, 0, 0, 0, 0, 23, 77, 26, 155, 40, 225, 212, 213, 24, 241, 153, 157, 79, 138, 196, 34, 184, 163, 164, 117, 80, 1, 245, 150, 94, 141, 5, 201, 51, 89, 254, 177, 0, 0, 0, 1, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 5, 0, 0, 0, 0, 1, 118, 182, 165, 0, 0, 0, 1, 0, 0, 0, 0, 101, 163, 177, 222, 16, 98, 2, 150, 222, 191, 160, 26, 169, 83, 228, 93, 221, 25, 210, 195, 158, 61, 172, 185, 169, 46, 106, 133, 202, 138, 48, 156, 0, 0, 0, 0, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 5, 0, 0, 0, 0, 0, 152, 150, 128, 0, 0, 0, 1, 0, 0, 0, 0, 111, 101, 34, 174, 82, 176, 35, 16, 118, 220, 99, 255, 149, 247, 234, 34, 226, 253, 128, 148, 62, 55, 35, 83, 2, 199, 238, 50, 175, 206, 76, 214, 0, 0, 0, 0, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 5, 0, 0, 0, 0, 0, 152, 150, 128, 0, 0, 0, 1, 0, 0, 0, 0, 132, 86, 73, 195, 209, 166, 48, 216, 180, 102, 247, 183, 39, 246, 87, 124, 180, 161, 120, 100, 105, 158, 109, 231, 86, 228, 132, 184, 29, 132, 205, 42, 0, 0, 0, 0, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 5, 0, 0, 0, 0, 0, 108, 43, 68, 0, 0, 0, 1, 0, 0, 0, 0, 209, 230, 72, 12, 24, 37, 25, 126, 46, 194, 147, 166, 11, 172, 220, 127, 96, 191, 186, 47, 60, 197, 56, 56, 85, 24, 11, 69, 213, 149, 167, 3, 0, 0, 0, 0, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 5, 0, 0, 0, 0, 0, 122, 18, 0, 0, 0, 0, 1, 0, 0, 0, 0, 245, 155, 154, 23, 94, 190, 76, 205, 141, 229, 220, 252, 106, 38, 135, 4, 20, 243, 12, 105, 108, 206, 25, 40, 63, 48, 20, 86, 36, 180, 69, 183, 0, 0, 0, 0, 33, 230, 115, 23, 203, 196, 190, 42, 235, 0, 103, 122, 214, 70, 39, 120, 168, 245, 34, 116, 185, 214, 5, 223, 37, 145, 178, 48, 39, 168, 125, 255, 0, 0, 0, 5, 0, 0, 0, 0, 1, 7, 164, 147, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0] ); } } diff --git a/libs/ur-registry/src/avalanche/avax_signature.rs b/libs/ur-registry/src/avalanche/avax_signature.rs index 14dc802..1436a2a 100644 --- a/libs/ur-registry/src/avalanche/avax_signature.rs +++ b/libs/ur-registry/src/avalanche/avax_signature.rs @@ -1,4 +1,4 @@ -use crate::cbor::cbor_map; +use crate::cbor::{cbor_map, cbor_array}; use crate::error::{URError, URResult}; use crate::registry_types::{RegistryType, AVAX_SIGNATURE, UUID}; use crate::traits::{From as FromCbor, RegistryItem, To}; @@ -15,7 +15,7 @@ const SIGNATURE: u8 = 2; #[derive(Clone, Debug, Default)] pub struct AvaxSignature { request_id: Bytes, - signature: Bytes, + signatures: Vec, } impl AvaxSignature { @@ -27,22 +27,22 @@ impl AvaxSignature { self.request_id = id; } - pub fn set_signature(&mut self, signature: Bytes) { - self.signature = signature; + pub fn set_signature(&mut self, signatures: Vec) { + self.signatures = signatures; } - pub fn new(request_id: Bytes, signature: Bytes) -> Self { + pub fn new(request_id: Bytes, signatures: Vec) -> Self { AvaxSignature { request_id, - signature, + signatures, } } pub fn get_request_id(&self) -> Bytes { self.request_id.clone() } - pub fn get_signature(&self) -> Bytes { - self.signature.clone() + pub fn get_signatures(&self) -> Vec { + self.signatures.clone() } } @@ -61,7 +61,12 @@ impl minicbor::Encode for AvaxSignature { let mut size = 2; e.map(size)?; e.int(Int::from(REQUEST_ID))?.bytes(&self.request_id)?; - e.int(Int::from(SIGNATURE))?.bytes(&self.signature)?; + + e.int(Int::from(SIGNATURE))? + .array(self.signatures.len() as u64)?; + for signature in &self.signatures { + e.bytes(signature)?; + } Ok(()) } } @@ -77,7 +82,13 @@ impl<'b, C> minicbor::Decode<'b, C> for AvaxSignature { obj.request_id = d.bytes()?.to_vec(); } SIGNATURE => { - obj.signature = d.bytes()?.to_vec(); + let signature_len = d.array()?; + obj.signatures = Vec::new(); + if signature_len.is_some() { + for _ in 0..signature_len.unwrap() { + obj.signatures.push(d.bytes()?.to_vec()); + } + } } _ => {} } @@ -101,33 +112,22 @@ impl FromCbor for AvaxSignature { #[cfg(test)] mod tests { - use crate::{avalanche::avax_signature::AvaxSignature, traits::{From as FromCbor, To}}; + use crate::{ + avalanche::avax_signature::AvaxSignature, + traits::{From as FromCbor, To}, + }; use alloc::vec::Vec; + use alloc::vec; use hex::FromHex; #[test] fn test_avax_signature_encode() { - let request_id = - [01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15].to_vec(); + let request_id = [01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15].to_vec(); let signature = hex::decode("80337c3a47f1b69a38544c69f379a4aa0ea8ef1f853b718d992c6a73c643e63ca6dff9186cd2f41a45c6405ef6b71353c3b6864c799699964e559afa7aa7f7c345c1966c998193539985e2724831025beadb0a1a269f54ec4a95c69a3bc4295a5c6c5f926dcc84fbf2251b56c841f764b162e062c8db5302090aa1d528d83cf48b53aa0709009f3975d63ea8ff26e80b4f2f01380e100860b304fccbbc0877278efbf72fb045331f76df132a5119bd51590f0502350d3cb31f14daba731893c5834e2e8bfa5bf517ac63693b81041cf7f8ed7293d034b3e54c4d02c66542d3b9648e9ecf912101a20b87f39d75d4f1a02c816f424c8a1fda05a9e7e8ccf064d31c0bf10c661872a7f40c0b1d75dbfae6a95ddcc81eead3f49cfa3803517cf9d79f2541041416c3e8ecfc0292d864f34fe613866e86b7b0bc7abc5b3f84e6ee3b06933c4f82552bb985f6b7fac0a580e94d7a0e8e295dd2e49ece66ead0ee6a46b84553302b94701a9d24b91c085154b7e67a7ac59e3a41ae96c8e1afd1aa778633457005555cff4198820c2aa8ea1ff0f86a9f4ae03d96b215449c63bff7cae9a114c9db05cc4e4d9993a13149393b6a6992b6042bb82d34ffdc7f1aeaf17fa5240ca6ebd9e62fd6c90bce91747af37bf8fc3c72859a1dfec2cf2c49295e1ccdc09b91d9074d204dea74a70002baa05fc86acfcff45fe7f0dd7e5e24c8f69575").unwrap(); - let avax_signature = AvaxSignature::new(request_id, signature); + let avax_signature = AvaxSignature::new(request_id, vec![signature]); assert_eq!( - "a2014f0102030405060708090a0b0c0d0e0f0259020080337c3a47f1b69a38544c69f379a4aa0ea8ef1f853b718d992c6a73c643e63ca6dff9186cd2f41a45c6405ef6b71353c3b6864c799699964e559afa7aa7f7c345c1966c998193539985e2724831025beadb0a1a269f54ec4a95c69a3bc4295a5c6c5f926dcc84fbf2251b56c841f764b162e062c8db5302090aa1d528d83cf48b53aa0709009f3975d63ea8ff26e80b4f2f01380e100860b304fccbbc0877278efbf72fb045331f76df132a5119bd51590f0502350d3cb31f14daba731893c5834e2e8bfa5bf517ac63693b81041cf7f8ed7293d034b3e54c4d02c66542d3b9648e9ecf912101a20b87f39d75d4f1a02c816f424c8a1fda05a9e7e8ccf064d31c0bf10c661872a7f40c0b1d75dbfae6a95ddcc81eead3f49cfa3803517cf9d79f2541041416c3e8ecfc0292d864f34fe613866e86b7b0bc7abc5b3f84e6ee3b06933c4f82552bb985f6b7fac0a580e94d7a0e8e295dd2e49ece66ead0ee6a46b84553302b94701a9d24b91c085154b7e67a7ac59e3a41ae96c8e1afd1aa778633457005555cff4198820c2aa8ea1ff0f86a9f4ae03d96b215449c63bff7cae9a114c9db05cc4e4d9993a13149393b6a6992b6042bb82d34ffdc7f1aeaf17fa5240ca6ebd9e62fd6c90bce91747af37bf8fc3c72859a1dfec2cf2c49295e1ccdc09b91d9074d204dea74a70002baa05fc86acfcff45fe7f0dd7e5e24c8f69575", + "a2014f0102030405060708090a0b0c0d0e0f028159020080337c3a47f1b69a38544c69f379a4aa0ea8ef1f853b718d992c6a73c643e63ca6dff9186cd2f41a45c6405ef6b71353c3b6864c799699964e559afa7aa7f7c345c1966c998193539985e2724831025beadb0a1a269f54ec4a95c69a3bc4295a5c6c5f926dcc84fbf2251b56c841f764b162e062c8db5302090aa1d528d83cf48b53aa0709009f3975d63ea8ff26e80b4f2f01380e100860b304fccbbc0877278efbf72fb045331f76df132a5119bd51590f0502350d3cb31f14daba731893c5834e2e8bfa5bf517ac63693b81041cf7f8ed7293d034b3e54c4d02c66542d3b9648e9ecf912101a20b87f39d75d4f1a02c816f424c8a1fda05a9e7e8ccf064d31c0bf10c661872a7f40c0b1d75dbfae6a95ddcc81eead3f49cfa3803517cf9d79f2541041416c3e8ecfc0292d864f34fe613866e86b7b0bc7abc5b3f84e6ee3b06933c4f82552bb985f6b7fac0a580e94d7a0e8e295dd2e49ece66ead0ee6a46b84553302b94701a9d24b91c085154b7e67a7ac59e3a41ae96c8e1afd1aa778633457005555cff4198820c2aa8ea1ff0f86a9f4ae03d96b215449c63bff7cae9a114c9db05cc4e4d9993a13149393b6a6992b6042bb82d34ffdc7f1aeaf17fa5240ca6ebd9e62fd6c90bce91747af37bf8fc3c72859a1dfec2cf2c49295e1ccdc09b91d9074d204dea74a70002baa05fc86acfcff45fe7f0dd7e5e24c8f69575", hex::encode(avax_signature.to_bytes().unwrap()).to_lowercase() ); } - - #[test] - fn test_avax_signature_decode() { - let bytes = Vec::from_hex( - "a2014f0102030405060708090a0b0c0d0e0f0259020080337c3a47f1b69a38544c69f379a4aa0ea8ef1f853b718d992c6a73c643e63ca6dff9186cd2f41a45c6405ef6b71353c3b6864c799699964e559afa7aa7f7c345c1966c998193539985e2724831025beadb0a1a269f54ec4a95c69a3bc4295a5c6c5f926dcc84fbf2251b56c841f764b162e062c8db5302090aa1d528d83cf48b53aa0709009f3975d63ea8ff26e80b4f2f01380e100860b304fccbbc0877278efbf72fb045331f76df132a5119bd51590f0502350d3cb31f14daba731893c5834e2e8bfa5bf517ac63693b81041cf7f8ed7293d034b3e54c4d02c66542d3b9648e9ecf912101a20b87f39d75d4f1a02c816f424c8a1fda05a9e7e8ccf064d31c0bf10c661872a7f40c0b1d75dbfae6a95ddcc81eead3f49cfa3803517cf9d79f2541041416c3e8ecfc0292d864f34fe613866e86b7b0bc7abc5b3f84e6ee3b06933c4f82552bb985f6b7fac0a580e94d7a0e8e295dd2e49ece66ead0ee6a46b84553302b94701a9d24b91c085154b7e67a7ac59e3a41ae96c8e1afd1aa778633457005555cff4198820c2aa8ea1ff0f86a9f4ae03d96b215449c63bff7cae9a114c9db05cc4e4d9993a13149393b6a6992b6042bb82d34ffdc7f1aeaf17fa5240ca6ebd9e62fd6c90bce91747af37bf8fc3c72859a1dfec2cf2c49295e1ccdc09b91d9074d204dea74a70002baa05fc86acfcff45fe7f0dd7e5e24c8f69575", - ) - .unwrap(); - let avax_signature = AvaxSignature::from_cbor(bytes).unwrap(); - assert_eq!( - [01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15].to_vec(), - avax_signature.get_request_id() - ); - assert_eq!(hex::decode("80337c3a47f1b69a38544c69f379a4aa0ea8ef1f853b718d992c6a73c643e63ca6dff9186cd2f41a45c6405ef6b71353c3b6864c799699964e559afa7aa7f7c345c1966c998193539985e2724831025beadb0a1a269f54ec4a95c69a3bc4295a5c6c5f926dcc84fbf2251b56c841f764b162e062c8db5302090aa1d528d83cf48b53aa0709009f3975d63ea8ff26e80b4f2f01380e100860b304fccbbc0877278efbf72fb045331f76df132a5119bd51590f0502350d3cb31f14daba731893c5834e2e8bfa5bf517ac63693b81041cf7f8ed7293d034b3e54c4d02c66542d3b9648e9ecf912101a20b87f39d75d4f1a02c816f424c8a1fda05a9e7e8ccf064d31c0bf10c661872a7f40c0b1d75dbfae6a95ddcc81eead3f49cfa3803517cf9d79f2541041416c3e8ecfc0292d864f34fe613866e86b7b0bc7abc5b3f84e6ee3b06933c4f82552bb985f6b7fac0a580e94d7a0e8e295dd2e49ece66ead0ee6a46b84553302b94701a9d24b91c085154b7e67a7ac59e3a41ae96c8e1afd1aa778633457005555cff4198820c2aa8ea1ff0f86a9f4ae03d96b215449c63bff7cae9a114c9db05cc4e4d9993a13149393b6a6992b6042bb82d34ffdc7f1aeaf17fa5240ca6ebd9e62fd6c90bce91747af37bf8fc3c72859a1dfec2cf2c49295e1ccdc09b91d9074d204dea74a70002baa05fc86acfcff45fe7f0dd7e5e24c8f69575").unwrap(), avax_signature.get_signature()); - } } diff --git a/libs/ur-registry/src/avalanche/avax_utxo.rs b/libs/ur-registry/src/avalanche/avax_utxo.rs new file mode 100644 index 0000000..3bd294a --- /dev/null +++ b/libs/ur-registry/src/avalanche/avax_utxo.rs @@ -0,0 +1,154 @@ +use crate::cbor::{cbor_array, cbor_map}; +use crate::error::{URError, URResult}; +use crate::registry_types::{RegistryType, AVAX_SIGN_REQUEST, UUID, CRYPTO_KEYPATH, AVAX_UTXO}; +use crate::crypto_key_path::CryptoKeyPath; +use crate::traits::{From as FromCbor, RegistryItem, To}; +use crate::types::{Bytes, Fingerprint}; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use minicbor::data::{Int, Tag}; +use minicbor::encode::Write; +use minicbor::{Decoder, Encoder}; + +const TX_ID: u8 = 1; +const OUTPUT_INDEX: u8 = 2; +const DERIVATION_PATH: u8 = 3; + +#[derive(Debug, Clone, Default)] +pub struct AvaxUtxo { + pub tx_id: Bytes, + pub output_index: u32, + derivation_path: CryptoKeyPath, +} + +impl AvaxUtxo { + pub fn new( + tx_id: Bytes, + output_index: u32, + derivation_path: CryptoKeyPath, + ) -> Self { + AvaxUtxo { + tx_id, + output_index, + derivation_path, + } + } + + pub fn get_tx_id(&self) -> Bytes { + self.tx_id.clone() + } + + pub fn get_output_index(&self) -> u32 { + self.output_index + } + + pub fn get_derivation_path(&self) -> CryptoKeyPath { + self.derivation_path.clone() + } +} + +impl RegistryItem for AvaxUtxo { + fn get_registry_type() -> RegistryType<'static> { + AVAX_UTXO + } +} + +impl minicbor::Encode for AvaxUtxo { + fn encode( + &self, + e: &mut Encoder, + _ctx: &mut C, + ) -> Result<(), minicbor::encode::Error> { + e.map(3)?; + e.int(Int::from(TX_ID))? + .tag(Tag::Unassigned(UUID.get_tag()))? + .bytes(&self.tx_id)?; + + e.int(Int::from(OUTPUT_INDEX))? + .int(Int::from(self.output_index))?; + e.int(Int::from(DERIVATION_PATH))?; + e.tag(Tag::Unassigned(CRYPTO_KEYPATH.get_tag()))?; + CryptoKeyPath::encode(&self.derivation_path, e, _ctx)?; + Ok(()) + } +} + +impl<'b, C> minicbor::Decode<'b, C> for AvaxUtxo { + fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result { + let mut result = AvaxUtxo::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 { + TX_ID => { + d.tag()?; + obj.tx_id = d.bytes()?.to_vec(); + } + OUTPUT_INDEX => { + obj.output_index = u32::try_from(d.int()?) + .map_err(|e| minicbor::decode::Error::message(e.to_string()))?; + } + DERIVATION_PATH => { + d.tag()?; + obj.derivation_path = CryptoKeyPath::decode(d, _ctx)?; + } + _ => {} + } + Ok(()) + })?; + Ok(result) + } +} + +impl To for AvaxUtxo { + fn to_bytes(&self) -> URResult> { + minicbor::to_vec(self.clone()).map_err(|e| URError::CborEncodeError(e.to_string())) + } +} + +impl FromCbor for AvaxUtxo { + fn from_cbor(bytes: Vec) -> URResult { + minicbor::decode(&bytes).map_err(|e| URError::CborDecodeError(e.to_string())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::RegistryItem; + use alloc::vec::Vec; + use hex::FromHex; + use crate::crypto_key_path::{CryptoKeyPath, PathComponent}; + use alloc::vec; + extern crate std; + use std::println; + + #[test] + fn test_avax_utxo_cbor() { + let tx_id = Vec::from_hex("a3b1c5d6e7f8091a2b3c4d5e6f708192a3b1c5d6e7f8091a2b3c4d5e6f708192").unwrap(); + let output_index = 1u32; + let components = vec![ + PathComponent::new(Some(44), true).unwrap(), + PathComponent::new(Some(9000), true).unwrap(), + PathComponent::new(Some(0), true).unwrap(), + PathComponent::new(Some(0), false).unwrap(), + PathComponent::new(Some(0), false).unwrap(), + ]; + + let derivation_path = CryptoKeyPath::new( + components, + Some(Fingerprint::from_hex("f23a9fd2").unwrap()), + None, + ); + + let utxo = AvaxUtxo::new(tx_id.clone(), output_index, derivation_path.clone()); + + let cbor_bytes = utxo.to_bytes().unwrap(); + + let decoded_utxo = AvaxUtxo::from_cbor(cbor_bytes).unwrap(); + + assert_eq!(decoded_utxo.get_tx_id(), tx_id); + assert_eq!(decoded_utxo.get_output_index(), output_index); + } +} diff --git a/libs/ur-registry/src/avalanche/mod.rs b/libs/ur-registry/src/avalanche/mod.rs index 19fb374..1b8a38f 100644 --- a/libs/ur-registry/src/avalanche/mod.rs +++ b/libs/ur-registry/src/avalanche/mod.rs @@ -1,2 +1,3 @@ pub mod avax_sign_request; -pub mod avax_signature; \ No newline at end of file +pub mod avax_signature; +mod avax_utxo; \ No newline at end of file diff --git a/libs/ur-registry/src/registry_types.rs b/libs/ur-registry/src/registry_types.rs index 908b380..ce6f29b 100644 --- a/libs/ur-registry/src/registry_types.rs +++ b/libs/ur-registry/src/registry_types.rs @@ -226,6 +226,7 @@ pub const XMR_TXSIGNED: RegistryType = RegistryType("xmr-txsigned", Some(8304)); pub const AVAX_SIGN_REQUEST: RegistryType = RegistryType("avax-sign-request", Some(8301)); pub const AVAX_SIGNATURE: RegistryType = RegistryType("avax-signature", Some(8302)); +pub const AVAX_UTXO: RegistryType = RegistryType("avax-utxo", Some(8303)); // Ergo pub const ERGO_SIGN_REQUEST: RegistryType = RegistryType("ergo-sign-request", Some(8401));