1use alloc::collections::{BTreeMap, BTreeSet};
4use bitcoin::PublicKey;
5pub use bitcoin::TxMerkleNode;
6use bitcoin::consensus::Decodable;
7use bitcoin::hashes::Hash;
8use bitcoin::io::Read;
9use serde::{Deserialize, Serialize};
10
11use crate::alloc::string::ToString;
12use alloc::{format, vec::Vec};
13
14use crate::asset::SerializedKey;
15use crate::commitment::TapscriptPreimage;
16use crate::error::Error;
17use crate::tlv::{Stream, Type};
18
19pub type RawProofFile = Vec<u8>;
22
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub struct CommitmentProof {
28 pub proof: crate::commitment::Proof,
31
32 pub tap_sibling_preimage: Option<TapscriptPreimage>,
36
37 pub stxo_proofs: BTreeMap<SerializedKey, crate::commitment::Proof>,
39
40 pub unknown_odd_types: BTreeMap<u64, Vec<u8>>,
47}
48
49const COMMITMENT_PROOF_TAP_SIBLING_PREIMAGE_TYPE: Type = Type(5);
52const COMMITMENT_PROOF_STXO_PROOFS_TYPE: Type = Type(7);
54impl CommitmentProof {
59 fn decode_tlv<R: Read>(r: R) -> Result<Self, Error> {
60 let mut stream = Stream::new(r);
61 let mut asset_proof: Option<crate::commitment::AssetProof> = None;
62 let mut taproot_asset_proof: Option<crate::commitment::TaprootAssetProof> = None;
63 let mut tap_sibling_preimage: Option<TapscriptPreimage> = None;
64 let mut stxo_proofs: Option<BTreeMap<SerializedKey, crate::commitment::Proof>> = None;
65 let mut unknown_odd_types = BTreeMap::new();
66
67 const PROOF_ASSET_PROOF_TYPE: Type = Type(0);
69 const PROOF_TAPROOT_ASSET_PROOF_TYPE: Type = Type(2);
70
71 while let Some(record) = stream.next_record().map_err(Error::TlvStream)? {
72 match record.tlv_type() {
73 PROOF_ASSET_PROOF_TYPE => {
75 asset_proof = Some(crate::commitment::AssetProof::decode_tlv(
76 record.value_reader(),
77 )?);
78 }
79 PROOF_TAPROOT_ASSET_PROOF_TYPE => {
80 taproot_asset_proof = Some(crate::commitment::TaprootAssetProof::decode_tlv(
81 record.value_reader(),
82 )?);
83 }
84 COMMITMENT_PROOF_TAP_SIBLING_PREIMAGE_TYPE => {
86 tap_sibling_preimage =
87 Some(TapscriptPreimage::decode_tlv(record.value_reader())?);
88 }
89 COMMITMENT_PROOF_STXO_PROOFS_TYPE => {
90 stxo_proofs = Some(decode_commitment_proofs(record.value_reader())?);
91 }
92 type_val => {
93 if type_val.is_odd() {
94 unknown_odd_types.insert(type_val.0, record.value().to_vec());
95 } else {
96 return Err(Error::UnknownTlvType(type_val.0));
98 }
99 }
100 }
101 }
102
103 let commitment_proof = crate::commitment::Proof {
105 asset_proof,
106 taproot_asset_proof: taproot_asset_proof.ok_or(Error::MissingTlvField(
107 "CommitmentProof.proof.taproot_asset_proof".to_string(),
108 ))?,
109 unknown_odd_types: BTreeMap::new(), };
111
112 Ok(CommitmentProof {
113 proof: commitment_proof,
114 tap_sibling_preimage,
115 stxo_proofs: stxo_proofs.unwrap_or_default(),
116 unknown_odd_types,
117 })
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125pub struct TapscriptProof {
126 pub tap_preimage1: Option<TapscriptPreimage>,
128
129 pub tap_preimage2: Option<TapscriptPreimage>,
132
133 pub bip86: bool,
135
136 pub unknown_odd_types: BTreeMap<u64, Vec<u8>>,
138}
139
140const TAPSCRIPT_PROOF_TAP_PREIMAGE1_TYPE: Type = Type(1);
142const TAPSCRIPT_PROOF_TAP_PREIMAGE2_TYPE: Type = Type(3);
143const TAPSCRIPT_PROOF_BIP86_TYPE: Type = Type(4);
144
145impl TapscriptProof {
146 fn decode_tlv<R: Read>(r: R) -> Result<Self, Error> {
147 let mut stream = Stream::new(r);
148 let mut tap_preimage1: Option<TapscriptPreimage> = None;
149 let mut tap_preimage2: Option<TapscriptPreimage> = None;
150 let mut bip86: Option<bool> = None;
151 let mut unknown_odd_types = BTreeMap::new();
152
153 while let Some(record) = stream.next_record().map_err(Error::TlvStream)? {
154 match record.tlv_type() {
155 TAPSCRIPT_PROOF_TAP_PREIMAGE1_TYPE => {
156 tap_preimage1 = Some(TapscriptPreimage::decode_tlv(record.value_reader())?);
157 }
158 TAPSCRIPT_PROOF_TAP_PREIMAGE2_TYPE => {
159 tap_preimage2 = Some(TapscriptPreimage::decode_tlv(record.value_reader())?);
160 }
161 TAPSCRIPT_PROOF_BIP86_TYPE => {
162 if record.value().len() != 1 {
163 return Err(Error::InvalidTlvValue(
164 TAPSCRIPT_PROOF_BIP86_TYPE.0,
165 "Length must be 1 for bool".to_string(),
166 ));
167 }
168 bip86 = Some(record.value()[0] != 0);
169 }
170 type_val => {
171 if type_val.is_odd() {
172 unknown_odd_types.insert(type_val.0, record.value().to_vec());
173 } else {
174 return Err(Error::UnknownTlvType(type_val.0));
175 }
176 }
177 }
178 }
179
180 Ok(TapscriptProof {
181 tap_preimage1,
182 tap_preimage2,
183 bip86: bip86.ok_or(Error::MissingTlvField("TapscriptProof.bip86".to_string()))?,
184 unknown_odd_types,
185 })
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
193pub struct TaprootProof {
194 pub output_index: u32,
196
197 pub internal_key: PublicKey,
199
200 pub commitment_proof: Option<CommitmentProof>,
203
204 pub tapscript_proof: Option<TapscriptProof>,
210
211 pub unknown_odd_types: BTreeMap<u64, Vec<u8>>,
218}
219
220const TAPROOT_PROOF_OUTPUT_INDEX_TYPE: Type = Type(0);
222const TAPROOT_PROOF_INTERNAL_KEY_TYPE: Type = Type(2);
223const TAPROOT_PROOF_COMMITMENT_PROOF_TYPE: Type = Type(3);
224const TAPROOT_PROOF_TAPSCRIPT_PROOF_TYPE: Type = Type(5);
225
226const MAX_NUM_TAPROOT_PROOFS: u64 = 1_000_000 / 43;
228const MAX_TAPROOT_PROOF_SIZE_BYTES: u64 = 65_535;
230
231impl TaprootProof {
232 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
234 Self::decode_tlv(bytes)
235 }
236
237 fn decode_tlv<R: Read>(r: R) -> Result<Self, Error> {
238 let mut stream = Stream::new(r);
239
240 let mut output_index: Option<u32> = None;
241 let mut internal_key: Option<PublicKey> = None;
242 let mut commitment_proof: Option<CommitmentProof> = None;
243 let mut tapscript_proof: Option<TapscriptProof> = None;
244 let mut unknown_odd_types = BTreeMap::new();
245
246 while let Some(record) = stream.next_record().map_err(Error::TlvStream)? {
247 match record.tlv_type() {
248 TAPROOT_PROOF_OUTPUT_INDEX_TYPE => {
249 let mut u32_bytes = [0u8; 4];
250 record
251 .value_reader()
252 .read_exact(&mut u32_bytes)
253 .map_err(Error::Io)?;
254 output_index = Some(u32::from_be_bytes(u32_bytes));
255 }
256 TAPROOT_PROOF_INTERNAL_KEY_TYPE => {
257 let pubkey_bytes: Vec<u8> = record.value().to_vec();
258 internal_key = Some(PublicKey::from_slice(&pubkey_bytes).map_err(|e| {
260 Error::BitcoinSerialization(format!("Invalid internal key: {}", e))
261 })?);
262 }
263 TAPROOT_PROOF_COMMITMENT_PROOF_TYPE => {
264 commitment_proof = Some(
265 CommitmentProof::decode_tlv(record.value_reader()).map_err(|e| {
266 Error::TlvStream(format!("CommitmentProof decode failed: {}", e))
267 })?,
268 );
269 }
270 TAPROOT_PROOF_TAPSCRIPT_PROOF_TYPE => {
271 tapscript_proof = Some(TapscriptProof::decode_tlv(record.value_reader())?);
272 }
273 type_val => {
274 if type_val.is_odd() {
275 unknown_odd_types.insert(type_val.0, record.value().to_vec());
276 } else {
277 return Err(Error::UnknownTlvType(type_val.0));
279 }
280 }
281 }
282 }
283
284 Ok(TaprootProof {
285 output_index: output_index.ok_or(Error::MissingTlvField(
286 "TaprootProof.output_index".to_string(),
287 ))?,
288 internal_key: internal_key.ok_or(Error::MissingTlvField(
289 "TaprootProof.internal_key".to_string(),
290 ))?,
291 commitment_proof,
292 tapscript_proof,
293 unknown_odd_types,
294 })
295 }
296}
297
298fn read_varint<R: Read>(r: &mut R) -> Result<u64, Error> {
300 let mut first_byte = [0u8; 1];
301 r.read_exact(&mut first_byte).map_err(Error::Io)?;
302
303 match first_byte[0] {
304 253 => {
305 let mut u16_bytes = [0u8; 2];
306 r.read_exact(&mut u16_bytes).map_err(Error::Io)?;
307 Ok(u16::from_be_bytes(u16_bytes) as u64)
308 }
309 254 => {
310 let mut u32_bytes = [0u8; 4];
311 r.read_exact(&mut u32_bytes).map_err(Error::Io)?;
312 Ok(u32::from_be_bytes(u32_bytes) as u64)
313 }
314 255 => {
315 let mut u64_bytes = [0u8; 8];
316 r.read_exact(&mut u64_bytes).map_err(Error::Io)?;
317 Ok(u64::from_be_bytes(u64_bytes))
318 }
319 _ => Ok(first_byte[0] as u64),
320 }
321}
322
323fn read_inline_var_bytes<R: Read>(r: &mut R, max_len: u64) -> Result<Vec<u8>, Error> {
325 let len = read_varint(r)?;
326 if len > max_len {
327 return Err(Error::BitcoinSerialization(format!(
328 "inline var bytes too large: {} (max: {})",
329 len, max_len
330 )));
331 }
332
333 let mut bytes = alloc::vec![0u8; len as usize];
334 r.read_exact(&mut bytes).map_err(Error::Io)?;
335 Ok(bytes)
336}
337
338fn decode_taproot_proofs<R: Read>(mut r: R) -> Result<Vec<TaprootProof>, Error> {
340 let num_proofs = read_varint(&mut r)?;
341 if num_proofs > MAX_NUM_TAPROOT_PROOFS {
342 return Err(Error::BitcoinSerialization(
343 "too many taproot proofs".to_string(),
344 ));
345 }
346
347 let mut proofs = Vec::with_capacity(num_proofs as usize);
348 for _ in 0..num_proofs {
349 let proof_bytes = read_inline_var_bytes(&mut r, MAX_TAPROOT_PROOF_SIZE_BYTES)?;
350 let proof = TaprootProof::decode_tlv(bitcoin::io::Cursor::new(&proof_bytes))?;
351 proofs.push(proof);
352 }
353
354 Ok(proofs)
355}
356
357fn decode_additional_inputs<R: Read>(mut r: R) -> Result<Vec<File>, Error> {
359 let num_inputs = read_varint(&mut r)?;
360 if num_inputs > u16::MAX as u64 {
361 return Err(Error::BitcoinSerialization(
362 "too many additional inputs".to_string(),
363 ));
364 }
365
366 let mut inputs = Vec::with_capacity(num_inputs as usize);
367 for _ in 0..num_inputs {
368 let input_bytes = read_inline_var_bytes(&mut r, FILE_MAX_SIZE_BYTES)?;
369 let input_file = File::from_bytes(&input_bytes)?;
370 inputs.push(input_file);
371 }
372
373 Ok(inputs)
374}
375
376fn decode_alt_leaves(bytes: &[u8]) -> Result<Vec<crate::asset::Asset>, Error> {
378 if bytes.len() as u64 > ALT_LEAVES_MAX_SIZE_BYTES {
379 return Err(Error::InvalidTlvValue(
380 PROOF_ALT_LEAVES_TYPE.0,
381 format!("alt leaves payload too large: {} bytes", bytes.len()),
382 ));
383 }
384
385 let mut cursor = bitcoin::io::Cursor::new(bytes);
386 let num_leaves = read_varint(&mut cursor)?;
387 let mut leaves = Vec::with_capacity(num_leaves as usize);
388 let mut leaf_keys = BTreeSet::new();
389
390 for _ in 0..num_leaves {
391 let leaf_bytes = read_inline_var_bytes(&mut cursor, ALT_LEAVES_MAX_SIZE_BYTES)?;
392 let leaf = crate::asset::decode_alt_leaf(&leaf_bytes)?;
393 if leaf.script_key.len() != 33 {
394 return Err(Error::InvalidTlvValue(
395 PROOF_ALT_LEAVES_TYPE.0,
396 format!(
397 "alt leaf script key length must be 33, got {}",
398 leaf.script_key.len()
399 ),
400 ));
401 }
402
403 let mut key_bytes = [0u8; 33];
404 key_bytes.copy_from_slice(&leaf.script_key);
405 let key = SerializedKey { bytes: key_bytes };
406 if !leaf_keys.insert(key) {
407 return Err(Error::InvalidTlvValue(
408 PROOF_ALT_LEAVES_TYPE.0,
409 "duplicate alt leaf script key".to_string(),
410 ));
411 }
412
413 leaves.push(leaf);
414 }
415
416 Ok(leaves)
417}
418
419fn decode_commitment_proofs<R: Read>(
421 mut r: R,
422) -> Result<BTreeMap<SerializedKey, crate::commitment::Proof>, Error> {
423 let num_proofs = read_varint(&mut r)?;
424 if num_proofs > MAX_NUM_TAPROOT_PROOFS {
425 return Err(Error::BitcoinSerialization(
426 "too many commitment proofs".to_string(),
427 ));
428 }
429
430 let mut proofs = BTreeMap::new();
431 for _ in 0..num_proofs {
432 let mut key_bytes = [0u8; 33];
433 r.read_exact(&mut key_bytes).map_err(Error::Io)?;
434
435 let proof_bytes = read_inline_var_bytes(&mut r, MAX_TAPROOT_PROOF_SIZE_BYTES)?;
436 let proof = crate::commitment::Proof::decode_tlv(bitcoin::io::Cursor::new(&proof_bytes))?;
437 proofs.insert(SerializedKey { bytes: key_bytes }, proof);
438 }
439
440 Ok(proofs)
441}
442
443fn decode_meta_reveal<R: Read>(r: R) -> Result<MetaReveal, Error> {
445 let mut stream = Stream::new(r);
446 let mut meta_type: Option<MetaType> = None;
447 let mut data: Option<Vec<u8>> = None;
448 let mut unknown_odd_types = BTreeMap::new();
449
450 while let Some(record) = stream.next_record().map_err(Error::TlvStream)? {
451 match record.tlv_type() {
452 META_REVEAL_ENCODING_TYPE => {
453 if record.value().len() != 1 {
454 return Err(Error::InvalidTlvValue(
455 META_REVEAL_ENCODING_TYPE.0,
456 "Length must be 1 for meta type".to_string(),
457 ));
458 }
459 meta_type = Some(match record.value()[0] {
460 0 => MetaType::Opaque,
461 1 => MetaType::Json,
462 other => {
463 return Err(Error::InvalidTlvValue(
464 META_REVEAL_ENCODING_TYPE.0,
465 format!("Unknown meta type: {}", other),
466 ));
467 }
468 });
469 }
470 META_REVEAL_DATA_TYPE => {
471 data = Some(record.value().to_vec());
472 }
473 type_val => {
474 if type_val.is_odd() {
475 unknown_odd_types.insert(type_val.0, record.value().to_vec());
476 } else {
477 return Err(Error::UnknownTlvType(type_val.0));
478 }
479 }
480 }
481 }
482
483 Ok(MetaReveal {
484 meta_type: meta_type.ok_or(Error::MissingTlvField("MetaReveal.meta_type".to_string()))?,
485 data: data.ok_or(Error::MissingTlvField("MetaReveal.data".to_string()))?,
486 unknown_odd_types,
487 })
488}
489
490#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
493pub struct TxMerkleProof {
494 pub nodes: Vec<TxMerkleNode>,
497
498 pub bits: Vec<bool>,
501}
502
503#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
505pub enum MetaType {
506 Opaque = 0,
508 Json = 1,
510}
511
512#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
514pub struct MetaReveal {
515 pub meta_type: MetaType,
517 pub data: Vec<u8>,
519 pub unknown_odd_types: BTreeMap<u64, Vec<u8>>,
521}
522
523impl TxMerkleProof {
524 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
525 Self::decode_tlv(bytes)
526 }
527
528 fn read_varint<R: Read>(r: &mut R) -> Result<u64, Error> {
530 let mut first_byte = [0u8; 1];
531 r.read_exact(&mut first_byte).map_err(Error::Io)?;
532
533 match first_byte[0] {
534 253 => {
535 let mut u16_bytes = [0u8; 2];
536 r.read_exact(&mut u16_bytes).map_err(Error::Io)?;
537 Ok(u16::from_be_bytes(u16_bytes) as u64)
538 }
539 254 => {
540 let mut u32_bytes = [0u8; 4];
541 r.read_exact(&mut u32_bytes).map_err(Error::Io)?;
542 Ok(u32::from_be_bytes(u32_bytes) as u64)
543 }
544 255 => {
545 let mut u64_bytes = [0u8; 8];
546 r.read_exact(&mut u64_bytes).map_err(Error::Io)?;
547 Ok(u64::from_be_bytes(u64_bytes))
548 }
549 _ => Ok(first_byte[0] as u64),
550 }
551 }
552
553 fn unpack_bits_from_slice(packed_bytes: &[u8]) -> Vec<bool> {
555 let mut bits = Vec::with_capacity(packed_bytes.len() * 8);
556 for i in 0..(packed_bytes.len() * 8) {
557 let byte_index = i / 8;
558 let bit_index = i % 8; let bit = (packed_bytes[byte_index] >> bit_index) & 1;
560 bits.push(bit == 1);
561 }
562 bits
563 }
564
565 fn decode_tlv<R: Read>(mut r: R) -> Result<Self, Error> {
573 const MERKLE_PROOF_MAX_NODES: u64 = 512;
574
575 let num_nodes = Self::read_varint(&mut r)?;
576
577 if num_nodes > MERKLE_PROOF_MAX_NODES {
578 return Err(Error::TlvStream(format!(
579 "Merkle proof has too many nodes: {}",
580 num_nodes
581 )));
582 }
583
584 let mut nodes = Vec::with_capacity(num_nodes as usize);
585 for _ in 0..num_nodes {
586 let mut hash_bytes = [0u8; 32];
587 r.read_exact(&mut hash_bytes).map_err(Error::Io)?;
588 nodes.push(TxMerkleNode::from_byte_array(hash_bytes));
589 }
590
591 let mut packed_bits = Vec::<u8>::new();
592 r.read_to_limit(&mut packed_bits, num_nodes)
593 .map_err(Error::Io)?;
594
595 let all_bits = Self::unpack_bits_from_slice(&packed_bits);
611
612 let bits = all_bits.into_iter().take(num_nodes as usize).collect();
614
615 Ok(TxMerkleProof { nodes, bits })
616 }
617}
618
619#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
620pub struct Proof {
621 pub version: u32,
623
624 pub prev_out: bitcoin::OutPoint,
628
629 pub block_header: bitcoin::block::Header,
632
633 pub block_height: u32,
636
637 pub anchor_tx: bitcoin::Transaction,
640
641 pub tx_merkle_proof: TxMerkleProof,
644
645 pub asset: crate::asset::Asset,
647
648 pub inclusion_proof: TaprootProof,
651
652 pub exclusion_proofs: Vec<TaprootProof>,
655
656 pub split_root_proof: Option<TaprootProof>,
660
661 pub meta_reveal: Option<MetaReveal>,
668
669 pub additional_inputs: Vec<File>,
672
673 pub challenge_witness: Option<bitcoin::Witness>,
680
681 pub genesis_reveal: Option<crate::asset::GenesisReveal>,
687
688 pub group_key_reveal: Option<crate::asset::GroupKeyReveal>,
695
696 pub alt_leaves: Vec<crate::asset::Asset>,
701
702 pub unknown_odd_types: BTreeMap<u64, Vec<u8>>,
709}
710
711const PROOF_VERSION_TYPE: Type = Type(0);
713const PROOF_PREV_OUT_TYPE: Type = Type(2);
714const PROOF_BLOCK_HEADER_TYPE: Type = Type(4);
715const PROOF_ANCHOR_TX_TYPE: Type = Type(6);
716const PROOF_TX_MERKLE_PROOF_TYPE: Type = Type(8);
717const PROOF_ASSET_LEAF_TYPE: Type = Type(10);
718const PROOF_INCLUSION_PROOF_TYPE: Type = Type(12);
719const PROOF_EXCLUSION_PROOFS_TYPE: Type = Type(13);
720const PROOF_SPLIT_ROOT_PROOF_TYPE: Type = Type(15);
721const PROOF_META_REVEAL_TYPE: Type = Type(17);
722const PROOF_ADDITIONAL_INPUTS_TYPE: Type = Type(19);
723const PROOF_CHALLENGE_WITNESS_TYPE: Type = Type(21);
724const PROOF_BLOCK_HEIGHT_TYPE: Type = Type(22);
725const PROOF_GENESIS_REVEAL_TYPE: Type = Type(23);
726const PROOF_GROUP_KEY_REVEAL_TYPE: Type = Type(25);
727const PROOF_ALT_LEAVES_TYPE: Type = Type(27);
728
729const META_REVEAL_ENCODING_TYPE: Type = Type(0);
731const META_REVEAL_DATA_TYPE: Type = Type(2);
733
734const ALT_LEAVES_MAX_SIZE_BYTES: u64 = u16::MAX as u64;
736
737const PROOF_PREFIX_MAGIC_BYTES: [u8; 4] = [0x54, 0x41, 0x50, 0x50]; impl Proof {
741 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
743 Self::decode_tlv(bytes)
744 }
745
746 fn decode_tlv<R: Read>(mut r: R) -> Result<Self, Error> {
747 let mut magic_bytes = [0u8; 4];
749 r.read_exact(&mut magic_bytes).map_err(Error::Io)?;
750
751 if magic_bytes != PROOF_PREFIX_MAGIC_BYTES {
752 return Err(Error::BitcoinSerialization(format!(
753 "Invalid proof magic bytes, expected {:?}, got {:?}",
754 PROOF_PREFIX_MAGIC_BYTES, magic_bytes
755 )));
756 }
757
758 let mut stream = Stream::new(r);
759
760 let mut version: Option<u32> = None;
761 let mut prev_out: Option<bitcoin::OutPoint> = None;
762 let mut block_header: Option<bitcoin::block::Header> = None;
763 let mut block_height: Option<u32> = None;
764 let mut anchor_tx: Option<bitcoin::Transaction> = None;
765 let mut tx_merkle_proof: Option<TxMerkleProof> = None;
766 let mut asset: Option<crate::asset::Asset> = None;
767 let mut inclusion_proof: Option<TaprootProof> = None;
768 let mut exclusion_proofs: Option<Vec<TaprootProof>> = None;
769 let mut split_root_proof: Option<TaprootProof> = None;
770 let mut meta_reveal: Option<MetaReveal> = None;
771 let mut additional_inputs: Option<Vec<File>> = None;
772 let mut challenge_witness: Option<bitcoin::Witness> = None;
773 let mut genesis_reveal: Option<crate::asset::GenesisReveal> = None;
774 let mut group_key_reveal: Option<crate::asset::GroupKeyReveal> = None;
775 let mut alt_leaves: Option<Vec<crate::asset::Asset>> = None;
776 let mut unknown_odd_types = BTreeMap::new();
777
778 while let Some(record) = stream.next_record().map_err(Error::TlvStream)? {
779 match record.tlv_type() {
780 PROOF_VERSION_TYPE => {
781 let mut u32_bytes = [0u8; 4];
782 record
783 .value_reader()
784 .read_exact(&mut u32_bytes)
785 .map_err(Error::Io)?;
786 version = Some(u32::from_be_bytes(u32_bytes));
787 }
788 PROOF_PREV_OUT_TYPE => {
789 let mut hash_bytes = [0u8; 32];
791 let mut reader = record.value_reader();
792 reader.read_exact(&mut hash_bytes).map_err(Error::Io)?;
793 let mut index_bytes = [0u8; 4];
794 reader.read_exact(&mut index_bytes).map_err(Error::Io)?;
795 let index = u32::from_be_bytes(index_bytes);
796 prev_out = Some(bitcoin::OutPoint {
797 txid: bitcoin::Txid::from_byte_array(hash_bytes),
798 vout: index,
799 });
800 }
801 PROOF_BLOCK_HEADER_TYPE => {
802 let header_bytes = record.value();
803 block_header = Some(
804 bitcoin::block::Header::consensus_decode(&mut bitcoin::io::Cursor::new(
805 header_bytes,
806 ))
807 .map_err(|e| {
808 Error::BitcoinSerialization(format!("Invalid block header: {}", e))
809 })?,
810 );
811 }
812 PROOF_BLOCK_HEIGHT_TYPE => {
813 let mut u32_bytes = [0u8; 4];
814 record
815 .value_reader()
816 .read_exact(&mut u32_bytes)
817 .map_err(Error::Io)?;
818 block_height = Some(u32::from_be_bytes(u32_bytes));
819 }
820 PROOF_ANCHOR_TX_TYPE => {
821 let tx_bytes = record.value();
823 anchor_tx = Some(
824 bitcoin::Transaction::consensus_decode(&mut bitcoin::io::Cursor::new(
825 tx_bytes,
826 ))
827 .map_err(|e| {
828 Error::BitcoinSerialization(format!(
829 "Invalid anchor transaction: {}",
830 e
831 ))
832 })?,
833 );
834 }
835 PROOF_TX_MERKLE_PROOF_TYPE => {
836 tx_merkle_proof = Some(TxMerkleProof::decode_tlv(record.value_reader())?);
837 }
838 PROOF_ASSET_LEAF_TYPE => {
839 asset = Some(crate::asset::Asset::decode_tlv(record.value_reader())?);
841 }
842 PROOF_INCLUSION_PROOF_TYPE => {
843 inclusion_proof = Some(TaprootProof::decode_tlv(record.value_reader())?);
844 }
845 PROOF_EXCLUSION_PROOFS_TYPE => {
846 exclusion_proofs = Some(decode_taproot_proofs(record.value_reader())?);
847 }
848 PROOF_SPLIT_ROOT_PROOF_TYPE => {
849 split_root_proof = Some(TaprootProof::decode_tlv(record.value_reader())?);
850 }
851 PROOF_META_REVEAL_TYPE => {
852 meta_reveal = Some(decode_meta_reveal(record.value_reader())?);
853 }
854 PROOF_ADDITIONAL_INPUTS_TYPE => {
855 additional_inputs = Some(decode_additional_inputs(record.value_reader())?);
856 }
857 PROOF_CHALLENGE_WITNESS_TYPE => {
858 let witness_bytes = record.value();
860 challenge_witness = Some(
861 bitcoin::Witness::consensus_decode(&mut bitcoin::io::Cursor::new(
862 witness_bytes,
863 ))
864 .map_err(|e| {
865 Error::BitcoinSerialization(format!("Invalid challenge witness: {}", e))
866 })?,
867 );
868 }
869 PROOF_GENESIS_REVEAL_TYPE => {
870 let genesis_info = crate::asset::decode_genesis_info(record.value_reader())
871 .map_err(|e| {
872 Error::TlvStream(format!("GenesisReveal decode failed: {}", e))
873 })?;
874 genesis_reveal = Some(crate::asset::GenesisReveal {
875 genesis_base: Some(genesis_info),
876 asset_type: crate::asset::AssetType::Normal,
877 amount: 0,
878 meta_reveal: None,
879 });
880 }
881 PROOF_GROUP_KEY_REVEAL_TYPE => {
882 group_key_reveal = Some(crate::asset::decode_group_key_reveal(record.value())?);
883 }
884 PROOF_ALT_LEAVES_TYPE => {
885 alt_leaves = Some(decode_alt_leaves(record.value())?);
886 }
887 type_val => {
888 if type_val.is_odd() {
889 unknown_odd_types.insert(type_val.0, record.value().to_vec());
890 } else {
891 return Err(Error::UnknownTlvType(type_val.0));
892 }
893 }
894 }
895 }
896
897 Ok(Proof {
898 version: version.ok_or(Error::MissingTlvField("Proof.version".to_string()))?,
899 prev_out: prev_out.ok_or(Error::MissingTlvField("Proof.prev_out".to_string()))?,
900 block_header: block_header
901 .ok_or(Error::MissingTlvField("Proof.block_header".to_string()))?,
902 block_height: block_height
903 .ok_or(Error::MissingTlvField("Proof.block_height".to_string()))?,
904 anchor_tx: anchor_tx.ok_or(Error::MissingTlvField("Proof.anchor_tx".to_string()))?,
905 tx_merkle_proof: tx_merkle_proof
906 .ok_or(Error::MissingTlvField("Proof.tx_merkle_proof".to_string()))?,
907 asset: asset.ok_or(Error::MissingTlvField("Proof.asset".to_string()))?,
908 inclusion_proof: inclusion_proof
909 .ok_or(Error::MissingTlvField("Proof.inclusion_proof".to_string()))?,
910 exclusion_proofs: exclusion_proofs.unwrap_or_default(),
911 split_root_proof,
912 meta_reveal,
913 additional_inputs: additional_inputs.unwrap_or_default(),
914 challenge_witness,
915 genesis_reveal,
916 group_key_reveal,
917 alt_leaves: alt_leaves.unwrap_or_default(),
918 unknown_odd_types,
919 })
920 }
921}
922
923#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
924pub struct File {
925 pub version: u32,
927
928 pub proofs: Vec<HashedProof>,
931}
932
933#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
936pub struct HashedProof {
937 pub proof_bytes: Vec<u8>,
939
940 pub hash: [u8; 32],
942}
943
944const FILE_MAX_NUM_PROOFS: u64 = 420000;
946const FILE_MAX_PROOF_SIZE_BYTES: u64 = 128 * 1024 * 1024; const FILE_MAX_SIZE_BYTES: u64 = 500 * 1024 * 1024;
949
950const FILE_PREFIX_MAGIC_BYTES: [u8; 4] = [0x54, 0x41, 0x50, 0x46]; impl File {
954 pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
956 Self::decode(bytes)
957 }
958
959 fn decode(bytes: &[u8]) -> Result<Self, Error> {
961 let mut cursor = bitcoin::io::Cursor::new(bytes);
962 Self::decode_from_reader(&mut cursor)
963 }
964
965 fn decode_from_reader<R: Read>(r: &mut R) -> Result<Self, Error> {
967 let mut magic_bytes = [0u8; 4];
969 r.read_exact(&mut magic_bytes).map_err(Error::Io)?;
970
971 if magic_bytes != FILE_PREFIX_MAGIC_BYTES {
972 return Err(Error::BitcoinSerialization(format!(
973 "Invalid file magic bytes, expected {:?}, got {:?}",
974 FILE_PREFIX_MAGIC_BYTES, magic_bytes
975 )));
976 }
977
978 let mut version_bytes = [0u8; 4];
980 r.read_exact(&mut version_bytes).map_err(Error::Io)?;
981 let version = u32::from_be_bytes(version_bytes);
982
983 let num_proofs = Self::read_varint(r)?;
985
986 if num_proofs > FILE_MAX_NUM_PROOFS {
988 return Err(Error::BitcoinSerialization(format!(
989 "Too many proofs in file: {} (max: {})",
990 num_proofs, FILE_MAX_NUM_PROOFS
991 )));
992 }
993
994 let mut proofs = Vec::with_capacity(num_proofs as usize);
995 let mut prev_hash = [0u8; 32]; for _ in 0..num_proofs {
998 let proof_size = Self::read_varint(r)?;
1000
1001 if proof_size > FILE_MAX_PROOF_SIZE_BYTES {
1003 return Err(Error::BitcoinSerialization(format!(
1004 "Proof in file too large: {} bytes (max: {})",
1005 proof_size, FILE_MAX_PROOF_SIZE_BYTES
1006 )));
1007 }
1008
1009 let mut proof_bytes = Vec::with_capacity(proof_size as usize);
1011 proof_bytes.resize(proof_size as usize, 0u8);
1012 r.read_exact(&mut proof_bytes).map_err(Error::Io)?;
1013
1014 let mut proof_hash = [0u8; 32];
1016 r.read_exact(&mut proof_hash).map_err(Error::Io)?;
1017
1018 let expected_hash = Self::hash_proof(&proof_bytes, &prev_hash);
1020
1021 if proof_hash != expected_hash {
1023 return Err(Error::BitcoinSerialization(
1024 "Invalid proof file checksum".to_string(),
1025 ));
1026 }
1027
1028 proofs.push(HashedProof {
1029 proof_bytes,
1030 hash: proof_hash,
1031 });
1032
1033 prev_hash = proof_hash;
1035 }
1036
1037 Ok(File { version, proofs })
1038 }
1039
1040 fn read_varint<R: Read>(r: &mut R) -> Result<u64, Error> {
1042 let mut first_byte = [0u8; 1];
1043 r.read_exact(&mut first_byte).map_err(Error::Io)?;
1044
1045 match first_byte[0] {
1046 253 => {
1047 let mut u16_bytes = [0u8; 2];
1048 r.read_exact(&mut u16_bytes).map_err(Error::Io)?;
1049 Ok(u16::from_be_bytes(u16_bytes) as u64)
1050 }
1051 254 => {
1052 let mut u32_bytes = [0u8; 4];
1053 r.read_exact(&mut u32_bytes).map_err(Error::Io)?;
1054 Ok(u32::from_be_bytes(u32_bytes) as u64)
1055 }
1056 255 => {
1057 let mut u64_bytes = [0u8; 8];
1058 r.read_exact(&mut u64_bytes).map_err(Error::Io)?;
1059 Ok(u64::from_be_bytes(u64_bytes))
1060 }
1061 _ => Ok(first_byte[0] as u64),
1062 }
1063 }
1064
1065 fn hash_proof(proof_bytes: &[u8], prev_hash: &[u8; 32]) -> [u8; 32] {
1068 use bitcoin::hashes::{Hash, sha256::Hash as Sha256Hash};
1069
1070 let mut combined = Vec::with_capacity(32 + proof_bytes.len());
1072 combined.extend_from_slice(prev_hash);
1073 combined.extend_from_slice(proof_bytes);
1074
1075 Sha256Hash::hash(&combined).to_byte_array()
1077 }
1078
1079 pub fn is_empty(&self) -> bool {
1081 self.proofs.is_empty()
1082 }
1083
1084 pub fn num_proofs(&self) -> usize {
1086 self.proofs.len()
1087 }
1088
1089 pub fn proof_at(&self, index: usize) -> Result<Proof, Error> {
1091 if index >= self.proofs.len() {
1092 return Err(Error::BitcoinSerialization(format!(
1093 "Invalid index {}",
1094 index
1095 )));
1096 }
1097
1098 Proof::from_bytes(&self.proofs[index].proof_bytes)
1099 }
1100
1101 pub fn last_proof(&self) -> Result<Proof, Error> {
1103 if self.is_empty() {
1104 return Err(Error::BitcoinSerialization(
1105 "No proof available".to_string(),
1106 ));
1107 }
1108
1109 self.proof_at(self.proofs.len() - 1)
1110 }
1111
1112 pub fn raw_proof_at(&self, index: usize) -> Result<Vec<u8>, Error> {
1114 if index >= self.proofs.len() {
1115 return Err(Error::BitcoinSerialization(format!(
1116 "Invalid index {}",
1117 index
1118 )));
1119 }
1120
1121 Ok(self.proofs[index].proof_bytes.clone())
1122 }
1123
1124 pub fn raw_last_proof(&self) -> Result<Vec<u8>, Error> {
1126 if self.is_empty() {
1127 return Err(Error::BitcoinSerialization(
1128 "No proof available".to_string(),
1129 ));
1130 }
1131
1132 self.raw_proof_at(self.proofs.len() - 1)
1133 }
1134}