1use crate::error::Error;
2use bitcoin::consensus::encode::serialize;
3use bitcoin::hashes::Hash;
4use bitcoin::hashes::sha256::Hash as Sha256Hash;
5use bitcoin::io::Read;
6use bitcoin::{BlockHash, OutPoint, Witness};
7use core::convert::TryFrom;
8
9use alloc::boxed::Box;
10use alloc::format;
11use alloc::string::{String, ToString};
12use alloc::vec::Vec;
13use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
14
15pub type AssetID = Sha256Hash;
16
17#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
19pub struct SerializedKey {
20 pub bytes: [u8; 33],
22}
23
24impl Serialize for SerializedKey {
25 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
27 where
28 S: Serializer,
29 {
30 serializer.serialize_bytes(&self.bytes)
31 }
32}
33
34impl<'de> Deserialize<'de> for SerializedKey {
35 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37 where
38 D: Deserializer<'de>,
39 {
40 struct BytesVisitor;
41
42 impl<'de> de::Visitor<'de> for BytesVisitor {
43 type Value = SerializedKey;
44
45 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
46 formatter.write_str("33 bytes")
47 }
48
49 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
50 where
51 E: de::Error,
52 {
53 if v.len() != 33 {
54 return Err(E::invalid_length(v.len(), &"33 bytes"));
55 }
56 let mut array = [0u8; 33];
57 array.copy_from_slice(v);
58 Ok(SerializedKey { bytes: array })
59 }
60
61 fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
62 where
63 E: de::Error,
64 {
65 self.visit_bytes(&v)
66 }
67
68 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
69 where
70 A: de::SeqAccess<'de>,
71 {
72 let mut bytes = [0u8; 33];
73 for i in 0..33 {
74 bytes[i] = seq
75 .next_element()?
76 .ok_or_else(|| de::Error::invalid_length(i, &"33 bytes"))?;
77 }
78 Ok(SerializedKey { bytes })
79 }
80 }
81
82 deserializer.deserialize_bytes(BytesVisitor)
83 }
84}
85
86#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
87#[repr(u8)]
88pub enum AssetVersion {
90 V0 = 0,
93 V1 = 1,
96}
97
98impl AssetVersion {
99 pub(crate) fn from_u8(val: u8) -> Result<Self, Error> {
100 match val {
101 0 => Ok(AssetVersion::V0),
102 1 => Ok(AssetVersion::V1),
103 _ => Err(Error::InvalidTlvValue(
104 0,
105 format!("Unknown AssetVersion: {}", val),
106 )),
107 }
108 }
109}
110
111impl TryFrom<i32> for AssetVersion {
112 type Error = String;
113
114 fn try_from(value: i32) -> core::result::Result<Self, Self::Error> {
115 match value {
116 0 => Ok(AssetVersion::V0),
117 1 => Ok(AssetVersion::V1),
118 _ => Err(format!("Invalid AssetVersion value: {}", value)),
119 }
120 }
121}
122
123#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
124pub enum AssetType {
126 Normal,
131 Collectible,
136}
137
138impl TryFrom<i32> for AssetType {
139 type Error = String;
140
141 fn try_from(value: i32) -> core::result::Result<Self, Self::Error> {
142 match value {
143 0 => Ok(AssetType::Normal),
144 1 => Ok(AssetType::Collectible),
145 _ => Err(format!("Invalid AssetType value: {}", value)),
146 }
147 }
148}
149
150#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
151pub enum ScriptKeyType {
153 Unknown,
158 Bip86,
162 ScriptPathExternal,
168 Burn,
173 Tombstone,
178 Channel,
186}
187
188impl TryFrom<i32> for ScriptKeyType {
189 type Error = String;
190
191 fn try_from(value: i32) -> core::result::Result<Self, Self::Error> {
192 match value {
193 0 => Ok(ScriptKeyType::Unknown),
194 1 => Ok(ScriptKeyType::Bip86),
195 2 => Ok(ScriptKeyType::ScriptPathExternal),
196 3 => Ok(ScriptKeyType::Burn),
197 4 => Ok(ScriptKeyType::Tombstone),
198 5 => Ok(ScriptKeyType::Channel),
199 _ => Err(format!("Invalid ScriptKeyType value: {}", value)),
200 }
201 }
202}
203
204#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
205pub struct GenesisInfo {
207 pub genesis_point: OutPoint,
209 pub name: String,
211 pub meta_hash: Sha256Hash,
213 pub asset_id: AssetID,
215 pub asset_type: AssetType,
217 pub output_index: u32,
221}
222
223#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
224pub struct AssetGroup {
226 pub raw_group_key: Vec<u8>,
228 pub tweaked_group_key: Vec<u8>,
232 pub asset_witness: Vec<u8>,
236 pub tapscript_root: Option<Sha256Hash>,
240}
241
242#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
243pub struct AnchorInfo {
245 pub anchor_tx: bitcoin::Transaction,
248 pub anchor_block_hash: BlockHash,
250 pub anchor_outpoint: OutPoint,
252 pub internal_key: Vec<u8>,
255 pub merkle_root: Sha256Hash,
260 pub tapscript_sibling: Vec<u8>,
265 pub block_height: u32,
267 pub block_timestamp: i64,
269}
270
271#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
272pub struct PrevInputAsset {
274 pub anchor_point: OutPoint,
276 pub asset_id: AssetID,
278 pub script_key: Vec<u8>,
280 pub amount: u64,
282}
283
284#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
285pub struct PrevId {
287 pub out_point: OutPoint,
289 pub asset_id: AssetID,
291 pub script_key: SerializedKey,
293}
294
295#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
296pub struct DecimalDisplay {
298 pub decimal_display: u32,
310}
311
312#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
313pub struct Asset {
315 pub version: AssetVersion,
317 pub asset_genesis: Option<GenesisInfo>,
319 pub amount: u64,
321 pub lock_time: i32,
323 pub relative_lock_time: i32,
325 pub script_version: i32,
327 pub script_key: Vec<u8>,
329 pub script_key_is_local: bool,
332 pub asset_group: Option<AssetGroup>,
334 pub chain_anchor: Option<AnchorInfo>,
336 pub prev_witnesses: Vec<PrevWitness>,
338 pub split_commitment_root: Option<crate::mssmt::MssmtNode>,
340 pub is_spent: bool,
342 pub lease_owner: Vec<u8>,
345 pub lease_expiry: i64,
348 pub is_burn: bool,
351 pub script_key_declared_known: bool,
361 pub script_key_has_script_path: bool,
369 pub decimal_display: Option<DecimalDisplay>,
373 pub script_key_type: ScriptKeyType,
378}
379
380#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
381pub enum AssetMetaType {
382 Opaque,
383 Json,
384}
385
386impl TryFrom<i32> for AssetMetaType {
387 type Error = String;
388
389 fn try_from(value: i32) -> core::result::Result<Self, Self::Error> {
390 match value {
391 0 => Ok(AssetMetaType::Opaque),
392 1 => Ok(AssetMetaType::Json),
393 _ => Err(format!("Invalid AssetMetaType value: {}", value)),
394 }
395 }
396}
397
398#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
399pub struct AssetMeta {
400 pub data: Vec<u8>,
401 pub meta_type: AssetMetaType,
402}
403
404#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
405pub struct GenesisReveal {
406 pub genesis_base: Option<GenesisInfo>,
407 pub asset_type: AssetType,
408 pub amount: u64,
409 pub meta_reveal: Option<AssetMeta>,
410}
411
412#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
413#[repr(u8)]
414pub enum NonSpendLeafVersion {
416 OpReturn = 1,
418 Pedersen = 2,
420}
421
422impl NonSpendLeafVersion {
423 pub(crate) fn from_u8(value: u8) -> Result<Self, Error> {
425 match value {
426 1 => Ok(NonSpendLeafVersion::OpReturn),
427 2 => Ok(NonSpendLeafVersion::Pedersen),
428 _ => Err(Error::InvalidTlvValue(
429 0,
430 format!("Unknown NonSpendLeafVersion: {}", value),
431 )),
432 }
433 }
434}
435
436#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
437pub struct GroupKeyReveal {
438 pub raw_group_key: SerializedKey,
440 pub tapscript_root: Option<Sha256Hash>,
442 pub version: Option<NonSpendLeafVersion>,
444 pub custom_subtree_root: Option<Sha256Hash>,
446}
447
448const GKR_VERSION_TYPE: crate::tlv::Type = crate::tlv::Type(0);
450const GKR_INTERNAL_KEY_TYPE: crate::tlv::Type = crate::tlv::Type(2);
452const GKR_TAPSCRIPT_ROOT_TYPE: crate::tlv::Type = crate::tlv::Type(4);
454const GKR_CUSTOM_SUBTREE_ROOT_TYPE: crate::tlv::Type = crate::tlv::Type(7);
456const GROUP_KEY_REVEAL_V0_MAX_LEN: u64 = 33 + 32;
458const GROUP_KEY_REVEAL_INTERNAL_KEY_LEN: usize = 33;
460const GROUP_KEY_REVEAL_TAPSCRIPT_ROOT_LEN: usize = 32;
462
463pub(crate) fn decode_group_key_reveal(bytes: &[u8]) -> Result<GroupKeyReveal, Error> {
465 let len = bytes.len() as u64;
466 if len <= GROUP_KEY_REVEAL_V0_MAX_LEN {
467 return decode_group_key_reveal_v0(bytes);
468 }
469
470 decode_group_key_reveal_v1(bytes)
471}
472
473fn decode_group_key_reveal_v0(bytes: &[u8]) -> Result<GroupKeyReveal, Error> {
475 let len = bytes.len() as u64;
476 if len > GROUP_KEY_REVEAL_V0_MAX_LEN {
477 return Err(Error::InvalidTlvValue(
478 0,
479 format!("GroupKeyRevealV0 too large: {}", len),
480 ));
481 }
482 if len < GROUP_KEY_REVEAL_INTERNAL_KEY_LEN as u64 {
483 return Err(Error::InvalidTlvValue(
484 0,
485 format!("GroupKeyRevealV0 too short: {}", len),
486 ));
487 }
488
489 let mut cursor = bitcoin::io::Cursor::new(bytes);
490 let mut key_bytes = [0u8; GROUP_KEY_REVEAL_INTERNAL_KEY_LEN];
491 cursor.read_exact(&mut key_bytes).map_err(Error::Io)?;
492 let raw_key = SerializedKey { bytes: key_bytes };
493
494 let remaining = len - GROUP_KEY_REVEAL_INTERNAL_KEY_LEN as u64;
495 let tapscript_root_bytes = read_inline_var_bytes(&mut cursor, remaining)?;
496 let tapscript_root = match tapscript_root_bytes.len() {
497 0 => None,
498 GROUP_KEY_REVEAL_TAPSCRIPT_ROOT_LEN => {
499 let mut root_bytes = [0u8; GROUP_KEY_REVEAL_TAPSCRIPT_ROOT_LEN];
500 root_bytes.copy_from_slice(&tapscript_root_bytes);
501 Some(Sha256Hash::from_byte_array(root_bytes))
502 }
503 other => {
504 return Err(Error::InvalidTlvValue(
505 0,
506 format!("Invalid V0 tapscript root length: {}", other),
507 ));
508 }
509 };
510
511 Ok(GroupKeyReveal {
512 raw_group_key: raw_key,
513 tapscript_root,
514 version: None,
515 custom_subtree_root: None,
516 })
517}
518
519fn decode_group_key_reveal_v1(bytes: &[u8]) -> Result<GroupKeyReveal, Error> {
521 let mut stream = crate::tlv::Stream::new(bitcoin::io::Cursor::new(bytes));
522 let mut version: Option<NonSpendLeafVersion> = None;
523 let mut internal_key: Option<SerializedKey> = None;
524 let mut tapscript_root: Option<Sha256Hash> = None;
525 let mut custom_subtree_root: Option<Sha256Hash> = None;
526
527 while let Some(record) = stream.next_record().map_err(Error::TlvStream)? {
528 match record.tlv_type() {
529 GKR_VERSION_TYPE => {
530 if record.value().len() != 1 {
531 return Err(Error::InvalidTlvValue(
532 GKR_VERSION_TYPE.0,
533 "Length must be 1 for group key reveal version".to_string(),
534 ));
535 }
536 version = Some(NonSpendLeafVersion::from_u8(record.value()[0])?);
537 }
538 GKR_INTERNAL_KEY_TYPE => {
539 if record.value().len() != GROUP_KEY_REVEAL_INTERNAL_KEY_LEN {
540 return Err(Error::InvalidTlvValue(
541 GKR_INTERNAL_KEY_TYPE.0,
542 "Invalid internal key length".to_string(),
543 ));
544 }
545 let mut key_bytes = [0u8; GROUP_KEY_REVEAL_INTERNAL_KEY_LEN];
546 key_bytes.copy_from_slice(record.value());
547 internal_key = Some(SerializedKey { bytes: key_bytes });
548 }
549 GKR_TAPSCRIPT_ROOT_TYPE => {
550 if record.value().len() != GROUP_KEY_REVEAL_TAPSCRIPT_ROOT_LEN {
551 return Err(Error::InvalidTlvValue(
552 GKR_TAPSCRIPT_ROOT_TYPE.0,
553 "Invalid tapscript root length".to_string(),
554 ));
555 }
556 let mut root_bytes = [0u8; GROUP_KEY_REVEAL_TAPSCRIPT_ROOT_LEN];
557 root_bytes.copy_from_slice(record.value());
558 tapscript_root = Some(Sha256Hash::from_byte_array(root_bytes));
559 }
560 GKR_CUSTOM_SUBTREE_ROOT_TYPE => {
561 if record.value().len() != GROUP_KEY_REVEAL_TAPSCRIPT_ROOT_LEN {
562 return Err(Error::InvalidTlvValue(
563 GKR_CUSTOM_SUBTREE_ROOT_TYPE.0,
564 "Invalid custom subtree root length".to_string(),
565 ));
566 }
567 let mut root_bytes = [0u8; GROUP_KEY_REVEAL_TAPSCRIPT_ROOT_LEN];
568 root_bytes.copy_from_slice(record.value());
569 custom_subtree_root = Some(Sha256Hash::from_byte_array(root_bytes));
570 }
571 type_val => {
572 if !type_val.is_odd() {
573 return Err(Error::UnknownTlvType(type_val.0));
574 }
575 }
576 }
577 }
578
579 Ok(GroupKeyReveal {
580 raw_group_key: internal_key.ok_or(Error::MissingTlvField(
581 "GroupKeyReveal.internal_key".to_string(),
582 ))?,
583 tapscript_root: Some(tapscript_root.ok_or(Error::MissingTlvField(
584 "GroupKeyReveal.tapscript_root".to_string(),
585 ))?),
586 version: Some(version.ok_or(Error::MissingTlvField("GroupKeyReveal.version".to_string()))?),
587 custom_subtree_root,
588 })
589}
590
591pub(crate) fn decode_alt_leaf(bytes: &[u8]) -> Result<Asset, Error> {
593 let mut prefixed = Vec::with_capacity(bytes.len() + 3);
594 prefixed.extend_from_slice(&[0u8, 1u8, 0u8]);
595 prefixed.extend_from_slice(bytes);
596 Asset::decode_tlv(bitcoin::io::Cursor::new(&prefixed))
597}
598
599#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
600pub struct SplitCommitment {
602 pub proof: crate::mssmt::MssmtProof,
604 pub root_asset: Box<Asset>,
606}
607
608#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
609pub struct PrevWitness {
611 pub prev_id: Option<PrevId>,
613 pub tx_witness: Witness,
615 pub split_commitment: Option<SplitCommitment>,
617}
618
619const ASSET_LEAF_VERSION: crate::tlv::Type = crate::tlv::Type(0);
621const ASSET_LEAF_GENESIS: crate::tlv::Type = crate::tlv::Type(2);
622const ASSET_LEAF_AMOUNT: crate::tlv::Type = crate::tlv::Type(6);
623const ASSET_LEAF_LOCK_TIME: crate::tlv::Type = crate::tlv::Type(7);
624const ASSET_LEAF_RELATIVE_LOCK_TIME: crate::tlv::Type = crate::tlv::Type(9);
625const ASSET_LEAF_PREV_WITNESS: crate::tlv::Type = crate::tlv::Type(11);
626const ASSET_LEAF_SPLIT_COMMITMENT_ROOT: crate::tlv::Type = crate::tlv::Type(13);
628const ASSET_LEAF_SCRIPT_VERSION: crate::tlv::Type = crate::tlv::Type(14);
629const ASSET_LEAF_SCRIPT_KEY: crate::tlv::Type = crate::tlv::Type(16);
630const ASSET_LEAF_GROUP_KEY: crate::tlv::Type = crate::tlv::Type(17);
631const ASSET_LEAF_TYPE: crate::tlv::Type = crate::tlv::Type(4);
632
633const WITNESS_PREV_ID: crate::tlv::Type = crate::tlv::Type(1);
635const WITNESS_TX_WITNESS: crate::tlv::Type = crate::tlv::Type(3);
637const WITNESS_SPLIT_COMMITMENT: crate::tlv::Type = crate::tlv::Type(5);
639
640const MAX_ASSET_NAME_LENGTH: u64 = 64;
642const MAX_WITNESS_STACK_ITEMS: u64 = u16::MAX as u64;
644const MAX_WITNESS_ELEMENT_SIZE: u64 = u16::MAX as u64;
646
647fn read_inline_var_bytes<R: bitcoin::io::Read>(
649 r: &mut R,
650 max_len: u64,
651) -> Result<Vec<u8>, crate::error::Error> {
652 let len = Asset::read_varint(r)?;
653 if len > max_len {
654 return Err(crate::error::Error::InvalidTlvValue(
655 0,
656 format!("inline var bytes too large: {} (max: {})", len, max_len),
657 ));
658 }
659
660 let mut bytes = alloc::vec![0u8; len as usize];
661 r.read_exact(&mut bytes).map_err(crate::error::Error::Io)?;
662 Ok(bytes)
663}
664
665fn decode_out_point<R: bitcoin::io::Read>(r: &mut R) -> Result<OutPoint, crate::error::Error> {
667 let mut hash_bytes = [0u8; 32];
668 r.read_exact(&mut hash_bytes)
669 .map_err(crate::error::Error::Io)?;
670 let mut index_bytes = [0u8; 4];
671 r.read_exact(&mut index_bytes)
672 .map_err(crate::error::Error::Io)?;
673
674 Ok(OutPoint {
675 txid: bitcoin::Txid::from_byte_array(hash_bytes),
676 vout: u32::from_be_bytes(index_bytes),
677 })
678}
679
680fn asset_type_byte(asset_type: AssetType) -> u8 {
682 match asset_type {
683 AssetType::Normal => 0,
684 AssetType::Collectible => 1,
685 }
686}
687
688fn compute_asset_id(genesis: &GenesisInfo) -> AssetID {
690 let outpoint_bytes = serialize(&genesis.genesis_point);
691 let tag_hash = Sha256Hash::hash(genesis.name.as_bytes());
692
693 let mut buf = Vec::with_capacity(outpoint_bytes.len() + 32 + 32 + 4 + 1);
694 buf.extend_from_slice(&outpoint_bytes);
695 buf.extend_from_slice(&tag_hash.to_byte_array());
696 buf.extend_from_slice(&genesis.meta_hash.to_byte_array());
697 buf.extend_from_slice(&genesis.output_index.to_be_bytes());
698 buf.push(asset_type_byte(genesis.asset_type));
699
700 Sha256Hash::hash(&buf)
701}
702
703pub(crate) fn decode_genesis_info<R: bitcoin::io::Read>(
705 mut r: R,
706) -> Result<GenesisInfo, crate::error::Error> {
707 let genesis_point = decode_out_point(&mut r)?;
708 let tag_bytes = read_inline_var_bytes(&mut r, MAX_ASSET_NAME_LENGTH)?;
709 let name = String::from_utf8(tag_bytes).map_err(|_| {
710 crate::error::Error::InvalidTlvValue(ASSET_LEAF_GENESIS.0, "invalid UTF-8 tag".to_string())
711 })?;
712
713 let mut meta_hash_bytes = [0u8; 32];
714 r.read_exact(&mut meta_hash_bytes)
715 .map_err(crate::error::Error::Io)?;
716 let meta_hash = Sha256Hash::from_byte_array(meta_hash_bytes);
717
718 let mut output_index_bytes = [0u8; 4];
719 r.read_exact(&mut output_index_bytes)
720 .map_err(crate::error::Error::Io)?;
721 let output_index = u32::from_be_bytes(output_index_bytes);
722
723 let mut asset_type_byte = [0u8; 1];
724 r.read_exact(&mut asset_type_byte)
725 .map_err(crate::error::Error::Io)?;
726 let asset_type = match asset_type_byte[0] {
727 0 => AssetType::Normal,
728 1 => AssetType::Collectible,
729 other => {
730 return Err(crate::error::Error::InvalidTlvValue(
731 ASSET_LEAF_TYPE.0,
732 format!("Unknown asset type: {}", other),
733 ));
734 }
735 };
736
737 let mut genesis = GenesisInfo {
738 genesis_point,
739 name,
740 meta_hash,
741 asset_id: AssetID::from_byte_array([0u8; 32]),
742 asset_type,
743 output_index,
744 };
745 genesis.asset_id = compute_asset_id(&genesis);
746
747 Ok(genesis)
748}
749
750fn decode_prev_id<R: bitcoin::io::Read>(mut r: R) -> Result<PrevId, crate::error::Error> {
752 let out_point = decode_out_point(&mut r)?;
753
754 let mut asset_id_bytes = [0u8; 32];
755 r.read_exact(&mut asset_id_bytes)
756 .map_err(crate::error::Error::Io)?;
757 let asset_id = AssetID::from_byte_array(asset_id_bytes);
758
759 let mut script_key_bytes = [0u8; 33];
760 r.read_exact(&mut script_key_bytes)
761 .map_err(crate::error::Error::Io)?;
762
763 Ok(PrevId {
764 out_point,
765 asset_id,
766 script_key: SerializedKey {
767 bytes: script_key_bytes,
768 },
769 })
770}
771
772fn decode_tx_witness<R: bitcoin::io::Read>(mut r: R) -> Result<Witness, crate::error::Error> {
774 let num_items = Asset::read_varint(&mut r)?;
775 if num_items > MAX_WITNESS_STACK_ITEMS {
776 return Err(crate::error::Error::InvalidTlvValue(
777 WITNESS_TX_WITNESS.0,
778 format!("too many witness items: {}", num_items),
779 ));
780 }
781
782 let mut items = Vec::with_capacity(num_items as usize);
783 for _ in 0..num_items {
784 let item = read_inline_var_bytes(&mut r, MAX_WITNESS_ELEMENT_SIZE)?;
785 items.push(item);
786 }
787
788 Ok(Witness::from(items))
789}
790
791fn decode_split_commitment<R: bitcoin::io::Read>(
793 mut r: R,
794 max_len: u64,
795) -> Result<SplitCommitment, crate::error::Error> {
796 let proof_bytes = read_inline_var_bytes(&mut r, max_len)?;
797 let proof = crate::mssmt::MssmtProof::decode_tlv(bitcoin::io::Cursor::new(&proof_bytes))?;
798
799 let root_asset_bytes = read_inline_var_bytes(&mut r, max_len)?;
800 let root_asset = Asset::decode_tlv(bitcoin::io::Cursor::new(&root_asset_bytes))?;
801
802 Ok(SplitCommitment {
803 proof,
804 root_asset: Box::new(root_asset),
805 })
806}
807
808fn decode_split_commitment_root<R: bitcoin::io::Read>(
810 mut r: R,
811) -> Result<crate::mssmt::MssmtNode, crate::error::Error> {
812 let mut hash_bytes = [0u8; 32];
813 r.read_exact(&mut hash_bytes)
814 .map_err(crate::error::Error::Io)?;
815 let mut sum_bytes = [0u8; 8];
816 r.read_exact(&mut sum_bytes)
817 .map_err(crate::error::Error::Io)?;
818
819 Ok(crate::mssmt::MssmtNode {
820 hash: Sha256Hash::from_byte_array(hash_bytes),
821 sum: u64::from_be_bytes(sum_bytes),
822 })
823}
824
825fn decode_witness<R: bitcoin::io::Read>(r: R) -> Result<PrevWitness, crate::error::Error> {
827 let mut stream = crate::tlv::Stream::new(r);
828
829 let mut prev_id: Option<PrevId> = None;
830 let mut tx_witness: Option<Witness> = None;
831 let mut split_commitment: Option<SplitCommitment> = None;
832
833 while let Some(record) = stream
834 .next_record()
835 .map_err(crate::error::Error::TlvStream)?
836 {
837 match record.tlv_type() {
838 WITNESS_PREV_ID => {
839 prev_id = Some(decode_prev_id(record.value_reader())?);
840 }
841 WITNESS_TX_WITNESS => {
842 tx_witness = Some(decode_tx_witness(record.value_reader())?);
843 }
844 WITNESS_SPLIT_COMMITMENT => {
845 let max_len = record.value().len() as u64;
846 split_commitment = Some(decode_split_commitment(record.value_reader(), max_len)?);
847 }
848 type_val => {
849 if !type_val.is_odd() {
850 return Err(crate::error::Error::UnknownTlvType(type_val.0));
851 }
852 }
853 }
854 }
855
856 Ok(PrevWitness {
857 prev_id,
858 tx_witness: tx_witness.unwrap_or_default(),
859 split_commitment,
860 })
861}
862
863fn decode_prev_witnesses<R: bitcoin::io::Read>(
865 mut r: R,
866) -> Result<Vec<PrevWitness>, crate::error::Error> {
867 let num_witnesses = Asset::read_varint(&mut r)?;
868 if num_witnesses > MAX_WITNESS_STACK_ITEMS {
869 return Err(crate::error::Error::InvalidTlvValue(
870 ASSET_LEAF_PREV_WITNESS.0,
871 format!("too many prev witnesses: {}", num_witnesses),
872 ));
873 }
874
875 let mut witnesses = Vec::with_capacity(num_witnesses as usize);
876 for _ in 0..num_witnesses {
877 let witness_bytes = read_inline_var_bytes(&mut r, MAX_WITNESS_ELEMENT_SIZE)?;
878 let witness = decode_witness(bitcoin::io::Cursor::new(&witness_bytes))?;
879 witnesses.push(witness);
880 }
881
882 Ok(witnesses)
883}
884
885impl Asset {
886 pub fn decode_tlv<R: bitcoin::io::Read>(r: R) -> Result<Self, crate::error::Error> {
888 let mut stream = crate::tlv::Stream::new(r);
889 let mut version: Option<AssetVersion> = None;
890 let mut genesis: Option<GenesisInfo> = None;
891 let mut amount: Option<u64> = None;
892 let mut lock_time: Option<u64> = None;
893 let mut relative_lock_time: Option<u64> = None;
894 let mut prev_witnesses: Option<Vec<PrevWitness>> = None;
895 let mut script_version: Option<u16> = None;
896 let mut script_key: Option<Vec<u8>> = None;
897 let mut group_key: Option<AssetGroup> = None;
898 let mut unknown_odd_types = alloc::collections::BTreeMap::new();
899 let mut split_commitment_root: Option<crate::mssmt::MssmtNode> = None;
900
901 while let Some(record) = stream
902 .next_record()
903 .map_err(crate::error::Error::TlvStream)?
904 {
905 match record.tlv_type() {
906 ASSET_LEAF_VERSION => {
907 if record.value().len() != 1 {
908 return Err(crate::error::Error::InvalidTlvValue(
909 ASSET_LEAF_VERSION.0,
910 String::from("Length must be 1 for version"),
911 ));
912 }
913 version = Some(AssetVersion::from_u8(record.value()[0])?);
914 }
915 ASSET_LEAF_GENESIS => {
916 genesis = Some(decode_genesis_info(record.value_reader())?);
917 }
918 ASSET_LEAF_AMOUNT => {
919 let mut cursor = bitcoin::io::Cursor::new(record.value());
921 amount = Some(Self::read_varint(&mut cursor)?);
922 }
923 ASSET_LEAF_LOCK_TIME => {
924 let mut cursor = bitcoin::io::Cursor::new(record.value());
926 let lock_time_val = Self::read_varint(&mut cursor)?;
927 lock_time = Some(lock_time_val);
928 }
929 ASSET_LEAF_RELATIVE_LOCK_TIME => {
930 let mut cursor = bitcoin::io::Cursor::new(record.value());
932 let relative_lock_time_val = Self::read_varint(&mut cursor)?;
933 relative_lock_time = Some(relative_lock_time_val);
934 }
935 ASSET_LEAF_PREV_WITNESS => {
936 prev_witnesses = Some(decode_prev_witnesses(record.value_reader())?);
937 }
938 ASSET_LEAF_SPLIT_COMMITMENT_ROOT => {
939 split_commitment_root =
940 Some(decode_split_commitment_root(record.value_reader())?);
941 }
942 ASSET_LEAF_SCRIPT_VERSION => {
943 if record.value().len() != 2 {
944 return Err(crate::error::Error::InvalidTlvValue(
945 ASSET_LEAF_SCRIPT_VERSION.0,
946 String::from("Length must be 2 for script version"),
947 ));
948 }
949 script_version =
950 Some(u16::from_be_bytes([record.value()[0], record.value()[1]]));
951 }
952 ASSET_LEAF_SCRIPT_KEY => {
953 if record.value().len() != 33 {
954 return Err(crate::error::Error::InvalidTlvValue(
955 ASSET_LEAF_SCRIPT_KEY.0,
956 String::from("Length must be 33 for script key"),
957 ));
958 }
959 script_key = Some(record.value().to_vec());
960 }
961 ASSET_LEAF_GROUP_KEY => {
962 if record.value().len() != 33 {
963 return Err(crate::error::Error::InvalidTlvValue(
964 ASSET_LEAF_GROUP_KEY.0,
965 String::from("Length must be 33 for group key"),
966 ));
967 }
968 group_key = Some(AssetGroup {
969 raw_group_key: record.value().to_vec(),
970 tweaked_group_key: Vec::new(),
971 asset_witness: Vec::new(),
972 tapscript_root: None,
973 });
974 }
975 ASSET_LEAF_TYPE => {
976 if record.value().len() != 1 {
977 return Err(crate::error::Error::InvalidTlvValue(
978 ASSET_LEAF_TYPE.0,
979 String::from("Length must be 1 for asset type"),
980 ));
981 }
982 let _asset_type = match record.value()[0] {
983 0 => AssetType::Normal,
984 1 => AssetType::Collectible,
985 _ => {
986 return Err(crate::error::Error::InvalidTlvValue(
987 ASSET_LEAF_TYPE.0,
988 format!("Unknown asset type: {}", record.value()[0]),
989 ));
990 }
991 };
992 }
993 type_val => {
994 if type_val.is_odd() {
995 unknown_odd_types.insert(type_val.0, record.value().to_vec());
996 } else {
997 return Err(crate::error::Error::UnknownTlvType(type_val.0));
998 }
999 }
1000 }
1001 }
1002
1003 Ok(Asset {
1004 version: version.ok_or(crate::error::Error::MissingTlvField(
1005 "Asset.version".to_string(),
1006 ))?,
1007 asset_genesis: genesis,
1008 amount: amount.unwrap_or(0),
1009 lock_time: lock_time.map(|v| v as i32).unwrap_or(0),
1010 relative_lock_time: relative_lock_time.map(|v| v as i32).unwrap_or(0),
1011 script_version: script_version.map(|v| v as i32).unwrap_or(0),
1012 script_key: script_key.unwrap_or_default(),
1013 script_key_is_local: false, asset_group: group_key,
1015 chain_anchor: None, prev_witnesses: prev_witnesses.unwrap_or_default(),
1017 split_commitment_root,
1018 is_spent: false, lease_owner: Vec::new(), lease_expiry: 0, is_burn: false, script_key_declared_known: false, script_key_has_script_path: false, decimal_display: None, script_key_type: ScriptKeyType::Unknown, })
1027 }
1028
1029 fn read_varint<R: bitcoin::io::Read>(r: &mut R) -> Result<u64, crate::error::Error> {
1031 let mut first_byte = [0u8; 1];
1032 r.read_exact(&mut first_byte)
1033 .map_err(crate::error::Error::Io)?;
1034
1035 match first_byte[0] {
1036 253 => {
1037 let mut u16_bytes = [0u8; 2];
1038 r.read_exact(&mut u16_bytes)
1039 .map_err(crate::error::Error::Io)?;
1040 Ok(u16::from_be_bytes(u16_bytes) as u64)
1041 }
1042 254 => {
1043 let mut u32_bytes = [0u8; 4];
1044 r.read_exact(&mut u32_bytes)
1045 .map_err(crate::error::Error::Io)?;
1046 Ok(u32::from_be_bytes(u32_bytes) as u64)
1047 }
1048 255 => {
1049 let mut u64_bytes = [0u8; 8];
1050 r.read_exact(&mut u64_bytes)
1051 .map_err(crate::error::Error::Io)?;
1052 Ok(u64::from_be_bytes(u64_bytes))
1053 }
1054 _ => Ok(first_byte[0] as u64),
1055 }
1056 }
1057}