use crate::Error;
use bitcoin::hashes::Hash;
use bitcoin::hex::DisplayHex;
use bitcoin::hex::FromHex;
use bitcoin::Txid;
use serde::Serialize;
use serde::Serializer;
use std::num::NonZeroU64;
pub mod packet;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AssetId {
pub txid: Txid,
pub group_index: u16,
}
impl AssetId {
fn encode(&self, buf: &mut Vec<u8>) {
let mut txid_bytes = self.txid.to_byte_array();
txid_bytes.reverse();
buf.extend_from_slice(&txid_bytes);
buf.extend_from_slice(&self.group_index.to_le_bytes());
}
}
impl std::fmt::Display for AssetId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}{}",
self.txid,
self.group_index.to_le_bytes().to_lower_hex_string()
)
}
}
impl Serialize for AssetId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(self)
}
}
impl std::str::FromStr for AssetId {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 68 {
return Err(Error::ad_hoc(format!(
"invalid asset ID format '{}', expected 68 hex chars (txid + 2-byte LE group index)",
s
)));
}
let txid: Txid = s[..64]
.parse()
.map_err(|e| Error::ad_hoc(format!("invalid txid in asset ID: {}", e)))?;
let group_index_bytes = <[u8; 2]>::from_hex(&s[64..])
.map_err(|e| Error::ad_hoc(format!("invalid group index in asset ID: {}", e)))?;
let group_index = u16::from_le_bytes(group_index_bytes);
Ok(Self { txid, group_index })
}
}
#[derive(Clone, Debug)]
pub enum ControlAssetConfig {
New {
amount: NonZeroU64,
},
Existing {
id: AssetId,
},
}
impl ControlAssetConfig {
pub fn new(amount: u64) -> Result<Self, Error> {
let amount =
NonZeroU64::new(amount).ok_or(Error::ad_hoc("control asset amount cannot be zero"))?;
Ok(Self::New { amount })
}
pub fn existing(id: AssetId) -> Self {
Self::Existing { id }
}
}