pub mod actions; pub mod documents; use crate::errors::*; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha512_256}; use std::{ borrow::Borrow, convert::{TryFrom, TryInto}, }; type ProtocolName = [char; 7]; pub const PROTOCOL_NAME: ProtocolName = ['G', 'M', 'i', 'x', 'e', 'r', '\0']; pub const PROTOCOL_VERSION: u16 = 0; pub const TX_ID_SEED_LEN: usize = 32; pub const TX_ID_LEN: usize = 32; pub const TX_ID_LIST_HASH_LEN: usize = 32; pub const TX_ID_COMMENT: &str = "GMixerTxId"; pub const TX_ID_LIST_HASH_COMMENT: &str = "GMixerTxIdListHash"; #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct ProtocolMark { pub name: ProtocolName, pub version: u16, pub document: u8, } impl ProtocolMark { pub fn new(document: u8) -> Self { Self { name: PROTOCOL_NAME, version: PROTOCOL_VERSION, document, } } } #[derive(Deserialize, Serialize)] pub struct ProtocolConfig { pub currency: String, /// Mix confirm validity duration pub mix_confirm_expire: u64, /// Mix demand validity duration pub mix_demand_expire: u64, /// Public key delegation validity duration pub pubkey_delegation_expire: u64, } impl Default for ProtocolConfig { fn default() -> Self { ProtocolConfig { currency: String::from("g1"), mix_confirm_expire: 604800, mix_demand_expire: 86400, pubkey_delegation_expire: 604800, } } } #[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct TxIdSeed(pub [u8; TX_ID_SEED_LEN]); impl TxIdSeed { pub fn generate() -> Self { Self(rand::random()) } pub fn from_string(raw: &str) -> Option<Self> { Some(Self( base64::decode_config(raw, base64::URL_SAFE_NO_PAD) .ok()? .try_into() .ok()?, )) } pub fn to_string(&self) -> String { base64::encode_config(self.0, base64::URL_SAFE_NO_PAD) } } impl AsRef<[u8]> for TxIdSeed { fn as_ref(&self) -> &[u8] { &self.0 } } impl Borrow<[u8]> for TxIdSeed { fn borrow(&self) -> &[u8] { &self.0 } } impl TryFrom<&[u8]> for TxIdSeed { type Error = std::array::TryFromSliceError; fn try_from(v: &[u8]) -> Result<Self, Self::Error> { Ok(Self(v.try_into()?)) } } #[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] pub struct TxId(pub [u8; TX_ID_LEN]); impl TxId { pub fn generate(tx_id_seeds: &[TxIdSeed; 3]) -> Self { let mut hasher = Sha512_256::new(); hasher.update(tx_id_seeds.concat()); Self(hasher.finalize().try_into().unwrap()) } pub fn to_comment(&self) -> String { format!( "{} {}", TX_ID_COMMENT, base64::encode_config(self.0, base64::URL_SAFE_NO_PAD) ) } } #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct TxIdListHash(pub [u8; TX_ID_LIST_HASH_LEN]); impl TxIdListHash { pub fn to_comment(&self) -> String { format!( "{} {}", TX_ID_LIST_HASH_COMMENT, base64::encode_config(self.0, base64::URL_SAFE_NO_PAD) ) } } impl AsRef<[u8]> for TxIdListHash { fn as_ref(&self) -> &[u8] { &self.0 } } #[derive(Debug, Eq, PartialEq)] pub enum DecodedComment { TxId(TxId), TxIdListHash(TxIdListHash), } pub fn decode_comment(comment: &str) -> Result<DecodedComment, Error> { let mut iter = comment.split(' '); let result: Option<DecodedComment> = try { match iter.next()? { TX_ID_COMMENT => DecodedComment::TxId(TxId( base64::decode_config(iter.next()?, base64::URL_SAFE_NO_PAD) .ok()? .try_into() .ok()?, )), TX_ID_LIST_HASH_COMMENT => DecodedComment::TxIdListHash(TxIdListHash( base64::decode_config(iter.next()?, base64::URL_SAFE_NO_PAD) .ok()? .try_into() .ok()?, )), _ => None?, } }; result.ok_or(Error::DeserializationError) }