Skip to main content

ark_core/
asset.rs

1use crate::Error;
2use bitcoin::hashes::Hash;
3use bitcoin::hex::DisplayHex;
4use bitcoin::hex::FromHex;
5use bitcoin::Txid;
6use serde::Serialize;
7use serde::Serializer;
8use std::num::NonZeroU64;
9
10pub mod packet;
11
12/// An asset identifier: (genesis_txid, group_index).
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub struct AssetId {
15    pub txid: Txid,
16    pub group_index: u16,
17}
18
19impl AssetId {
20    fn encode(&self, buf: &mut Vec<u8>) {
21        // txid in its canonical display byte order, followed by group_index as LE bytes.
22        let mut txid_bytes = self.txid.to_byte_array();
23        txid_bytes.reverse();
24        buf.extend_from_slice(&txid_bytes);
25        buf.extend_from_slice(&self.group_index.to_le_bytes());
26    }
27}
28
29impl std::fmt::Display for AssetId {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        write!(
32            f,
33            "{}{}",
34            self.txid,
35            self.group_index.to_le_bytes().to_lower_hex_string()
36        )
37    }
38}
39
40impl Serialize for AssetId {
41    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
42    where
43        S: Serializer,
44    {
45        serializer.collect_str(self)
46    }
47}
48
49impl std::str::FromStr for AssetId {
50    type Err = Error;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        if s.len() != 68 {
54            return Err(Error::ad_hoc(format!(
55                "invalid asset ID format '{}', expected 68 hex chars (txid + 2-byte LE group index)",
56                s
57            )));
58        }
59
60        let txid: Txid = s[..64]
61            .parse()
62            .map_err(|e| Error::ad_hoc(format!("invalid txid in asset ID: {}", e)))?;
63
64        let group_index_bytes = <[u8; 2]>::from_hex(&s[64..])
65            .map_err(|e| Error::ad_hoc(format!("invalid group index in asset ID: {}", e)))?;
66        let group_index = u16::from_le_bytes(group_index_bytes);
67
68        Ok(Self { txid, group_index })
69    }
70}
71
72/// Control asset configuration to issue new assets.
73#[derive(Clone, Debug)]
74pub enum ControlAssetConfig {
75    /// Issue an asset with a new control asset.
76    New {
77        /// Number of control asset units to create.
78        amount: NonZeroU64,
79    },
80    /// Issue an asset with an existing control asset.
81    Existing {
82        /// Control asset ID.
83        id: AssetId,
84    },
85}
86
87impl ControlAssetConfig {
88    /// Instantiate control asset config to issue assets with a _new_ control asset.
89    ///
90    /// # Arguments
91    ///
92    /// * `amount` - The number of _control_ asset units to issue.
93    pub fn new(amount: u64) -> Result<Self, Error> {
94        let amount =
95            NonZeroU64::new(amount).ok_or(Error::ad_hoc("control asset amount cannot be zero"))?;
96
97        Ok(Self::New { amount })
98    }
99
100    /// Instantiate control asset config to issue assets with an _existing_ control asset.
101    ///
102    /// # Arguments
103    ///
104    /// * `id` - The existing control asset ID.
105    pub fn existing(id: AssetId) -> Self {
106        Self::Existing { id }
107    }
108}