1use crate::asset::AssetVersion;
2use crate::error::Error;
3use crate::mssmt::MssmtProof;
4use crate::tlv::{Stream, Type};
5use alloc::collections::BTreeMap;
6use bitcoin::io::Read;
7
8use crate::alloc::string::ToString;
9use alloc::vec::Vec;
10
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
17#[repr(u8)]
18pub enum TapCommitmentVersion {
19 V0 = 0,
21 V1 = 1,
23 V2 = 2,
25}
26
27impl TapCommitmentVersion {
28 pub(crate) fn from_u8(val: u8) -> Result<Self, Error> {
29 match val {
30 0 => Ok(TapCommitmentVersion::V0),
31 1 => Ok(TapCommitmentVersion::V1),
32 2 => Ok(TapCommitmentVersion::V2),
33 _ => Err(Error::InvalidTlvValue(
34 0,
35 alloc::format!("Unknown TapCommitmentVersion: {}", val),
36 )),
37 }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
44#[repr(u8)]
45pub enum TapscriptPreimageType {
46 LeafPreimage = 0,
48 BranchPreimage = 1,
50}
51
52impl TapscriptPreimageType {
53 pub(crate) fn from_u8(val: u8) -> Result<Self, Error> {
54 match val {
55 0 => Ok(TapscriptPreimageType::LeafPreimage),
56 1 => Ok(TapscriptPreimageType::BranchPreimage),
57 _ => Err(Error::InvalidTlvValue(
58 0,
59 alloc::format!("Unknown TapscriptPreimageType: {}", val),
60 )),
61 }
62 }
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
68pub struct TapscriptPreimage {
69 pub sibling_preimage: Vec<u8>,
72 pub sibling_type: TapscriptPreimageType,
74}
75
76impl TapscriptPreimage {
77 pub fn decode_tlv<R: Read>(mut r: R) -> Result<Self, Error> {
80 let mut type_buf = [0u8; 1];
81 r.read_exact(&mut type_buf).map_err(Error::Io)?;
82 let sibling_type_byte = type_buf[0];
83 let sibling_type = TapscriptPreimageType::from_u8(sibling_type_byte)?;
84
85 let mut sibling_preimage = Vec::new();
86 let mut chunk = [0u8; 512]; loop {
89 match r.read(&mut chunk) {
90 Ok(0) => break, Ok(n) => sibling_preimage.extend_from_slice(&chunk[..n]),
92 Err(e) => return Err(Error::Io(e)), }
94 }
95
96 match sibling_type {
98 TapscriptPreimageType::BranchPreimage if sibling_preimage.len() != 64 => {
99 return Err(Error::InvalidTlvValue(
100 sibling_type_byte as u64,
101 "BranchPreimage must be 64 bytes".to_string(),
102 ));
103 }
104 _ => {}
105 }
106
107 Ok(TapscriptPreimage {
108 sibling_preimage,
109 sibling_type,
110 })
111 }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub struct AssetProof {
118 pub proof: MssmtProof,
120 pub version: AssetVersion,
122 pub tap_key: [u8; 32],
125 pub unknown_odd_types: BTreeMap<u64, Vec<u8>>,
127}
128
129impl AssetProof {
130 pub fn decode_tlv<R: Read>(r: R) -> Result<Self, Error> {
131 let mut stream = Stream::new(r);
132 let mut mssmt_proof: Option<MssmtProof> = None;
133 let mut version: Option<AssetVersion> = None;
134 let mut tap_key: Option<[u8; 32]> = None;
135 let mut unknown_odd_types = BTreeMap::new();
136
137 while let Some(record) = stream.next_record().map_err(Error::TlvStream)? {
138 match record.tlv_type() {
139 ASSET_PROOF_MSSMT_PROOF_TYPE => {
140 mssmt_proof = Some(MssmtProof::decode_tlv(record.value_reader())?);
141 }
142 ASSET_PROOF_VERSION_TYPE => {
143 if record.value().len() != 1 {
144 return Err(Error::InvalidTlvValue(
145 ASSET_PROOF_VERSION_TYPE.0,
146 "Length must be 1 for AssetVersion".to_string(),
147 ));
148 }
149 version = Some(AssetVersion::from_u8(record.value()[0])?);
150 }
151 ASSET_PROOF_TAP_KEY_TYPE => {
152 if record.value().len() != 32 {
153 return Err(Error::InvalidTlvValue(
154 ASSET_PROOF_TAP_KEY_TYPE.0,
155 "Length must be 32 for TapKey".to_string(),
156 ));
157 }
158 let mut key_bytes = [0u8; 32];
159 key_bytes.copy_from_slice(record.value());
160 tap_key = Some(key_bytes);
161 }
162 type_val => {
163 if type_val.is_odd() {
164 unknown_odd_types.insert(type_val.0, record.value().to_vec());
165 } else {
166 return Err(Error::UnknownTlvType(type_val.0));
167 }
168 }
169 }
170 }
171 Ok(AssetProof {
172 proof: mssmt_proof.ok_or(Error::MissingTlvField("AssetProof.proof".to_string()))?,
173 version: version.ok_or(Error::MissingTlvField("AssetProof.version".to_string()))?,
174 tap_key: tap_key.ok_or(Error::MissingTlvField("AssetProof.tap_key".to_string()))?,
175 unknown_odd_types,
176 })
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
183pub struct TaprootAssetProof {
184 pub proof: MssmtProof,
186 pub version: TapCommitmentVersion,
188 pub unknown_odd_types: BTreeMap<u64, Vec<u8>>,
190}
191
192impl TaprootAssetProof {
193 pub fn decode_tlv<R: Read>(r: R) -> Result<Self, Error> {
194 let mut stream = Stream::new(r);
195 let mut mssmt_proof: Option<MssmtProof> = None;
196 let mut version: Option<TapCommitmentVersion> = None;
197 let mut unknown_odd_types = BTreeMap::new();
198
199 while let Some(record) = stream.next_record().map_err(Error::TlvStream)? {
200 match record.tlv_type() {
201 TAPROOT_ASSET_PROOF_MSSMT_PROOF_TYPE => {
202 mssmt_proof = Some(MssmtProof::decode_tlv(record.value_reader())?);
203 }
204 TAPROOT_ASSET_PROOF_VERSION_TYPE => {
205 if record.value().len() != 1 {
206 return Err(Error::InvalidTlvValue(
207 TAPROOT_ASSET_PROOF_VERSION_TYPE.0,
208 "Length must be 1 for TapCommitmentVersion".to_string(),
209 ));
210 }
211 version = Some(TapCommitmentVersion::from_u8(record.value()[0])?);
212 }
213 type_val => {
214 if type_val.is_odd() {
215 unknown_odd_types.insert(type_val.0, record.value().to_vec());
216 } else {
217 return Err(Error::UnknownTlvType(type_val.0));
218 }
219 }
220 }
221 }
222 Ok(TaprootAssetProof {
223 proof: mssmt_proof.ok_or(Error::MissingTlvField(
224 "TaprootAssetProof.proof".to_string(),
225 ))?,
226 version: version.ok_or(Error::MissingTlvField(
227 "TaprootAssetProof.version".to_string(),
228 ))?,
229 unknown_odd_types,
230 })
231 }
232}
233
234#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
238pub struct Proof {
239 pub asset_proof: Option<AssetProof>,
243 pub taproot_asset_proof: TaprootAssetProof,
246 pub unknown_odd_types: BTreeMap<u64, Vec<u8>>,
248}
249
250impl Proof {
251 pub fn decode_tlv<R: Read>(r: R) -> Result<Self, Error> {
252 let mut stream = Stream::new(r);
253 let mut asset_proof: Option<AssetProof> = None;
254 let mut taproot_asset_proof: Option<TaprootAssetProof> = None;
255 let mut unknown_odd_types = BTreeMap::new();
256
257 while let Some(record) = stream.next_record().map_err(Error::TlvStream)? {
258 match record.tlv_type() {
259 PROOF_ASSET_PROOF_TYPE => {
260 asset_proof = Some(AssetProof::decode_tlv(record.value_reader())?);
261 }
262 PROOF_TAPROOT_ASSET_PROOF_TYPE => {
263 taproot_asset_proof =
264 Some(TaprootAssetProof::decode_tlv(record.value_reader())?);
265 }
266 type_val => {
267 if type_val.is_odd() {
268 unknown_odd_types.insert(type_val.0, record.value().to_vec());
269 } else {
270 return Err(Error::UnknownTlvType(type_val.0));
271 }
272 }
273 }
274 }
275 Ok(Proof {
276 asset_proof,
277 taproot_asset_proof: taproot_asset_proof.ok_or(Error::MissingTlvField(
278 "Proof.taproot_asset_proof".to_string(),
279 ))?,
280 unknown_odd_types,
281 })
282 }
283}
284
285const PROOF_ASSET_PROOF_TYPE: Type = Type(0);
289const PROOF_TAPROOT_ASSET_PROOF_TYPE: Type = Type(2);
290
291const ASSET_PROOF_VERSION_TYPE: Type = Type(0);
293const ASSET_PROOF_TAP_KEY_TYPE: Type = Type(2); const ASSET_PROOF_MSSMT_PROOF_TYPE: Type = Type(4); const TAPROOT_ASSET_PROOF_VERSION_TYPE: Type = Type(0);
298const TAPROOT_ASSET_PROOF_MSSMT_PROOF_TYPE: Type = Type(2);