Skip to main content

elements/transaction/
pegin_witness.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Pegin Witnesses
4
5use core::fmt;
6use std::io;
7
8// lol should these be accessible from a more convenient path?
9use bitcoin::block::BlockHashDecoder as GenesisHashDecoder;
10use bitcoin::block::{
11    BlockHashDecoderError as GenesisHashDecoderError, BlockHashEncoder as GenesisHashEncoder,
12};
13use bitcoin::hashes::Hash as _;
14use hashes::encoding::UnexpectedEofError;
15
16use crate::{encode, encoding, AssetId};
17
18const TOTAL_PEGIN_LENGTH: usize = 6;
19
20/// A pegin witness, which must be either a stack of 6 well-formed pieces of pegin data, or an empty witness stack.
21#[derive(Default, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
22pub struct PeginWitness {
23    inner: Option<PeginData>,
24}
25
26impl PeginWitness {
27    /// An empty pegin witness.
28    pub const EMPTY: Self = Self { inner: None };
29
30    /// A pegin witness containing the given data.
31    pub fn new(data: PeginData) -> Self { Self { inner: Some(data) } }
32
33    /// Accessor for the pegin witness data.
34    pub fn data(&self) -> Option<&PeginData> { self.inner.as_ref() }
35
36    /// Accessor for the pegin witness data.
37    pub fn into_data(self) -> Option<PeginData> { self.inner }
38
39    /// Whether this pegin witness is empty (no pegin).
40    pub fn is_empty(&self) -> bool { self.inner.is_none() }
41
42    /// The number of witness elements in the [`PeginWitness`].
43    pub fn len(&self) -> usize {
44        if self.inner.is_some() {
45            TOTAL_PEGIN_LENGTH
46        } else {
47            0
48        }
49    }
50
51    /// The number of bytes used to encode the [`PeginWitness`].
52    pub fn encoded_size(&self) -> usize {
53        use encoding::{Encode as _, ExactSizeEncoder as _};
54        match self.inner {
55            Some(ref data) => data.encoder().len(),
56            None => 1,
57        }
58    }
59}
60
61/// Encoder for the [`PeginWitness`] type.
62#[derive(Clone, Debug)]
63pub struct PeginWitnessEncoder<'e>(PeginWitnessEncoderInner<'e>);
64
65#[derive(Clone, Debug)]
66#[allow(clippy::large_enum_variant)]
67enum PeginWitnessEncoderInner<'e> {
68    Pegin(PeginDataEncoder<'e>),
69    Empty(encoding::CompactSizeEncoder),
70}
71
72impl encoding::Encoder for PeginWitnessEncoder<'_> {
73    fn current_chunk(&self) -> &[u8] {
74        use PeginWitnessEncoderInner as Inner;
75        match self.0 {
76            Inner::Pegin(ref enc) => enc.current_chunk(),
77            Inner::Empty(ref enc) => enc.current_chunk(),
78        }
79    }
80
81    fn advance(&mut self) -> encoding::EncoderStatus {
82        use PeginWitnessEncoderInner as Inner;
83        match self.0 {
84            Inner::Pegin(ref mut enc) => enc.advance(),
85            Inner::Empty(ref mut enc) => enc.advance(),
86        }
87    }
88}
89
90impl encoding::ExactSizeEncoder for PeginWitnessEncoder<'_> {
91    fn len(&self) -> usize {
92        use PeginWitnessEncoderInner as Inner;
93        match self.0 {
94            Inner::Pegin(ref enc) => enc.len(),
95            Inner::Empty(ref enc) => enc.len(),
96        }
97    }
98}
99
100impl encoding::Encode for PeginWitness {
101    type Encoder<'e> = PeginWitnessEncoder<'e>;
102
103    fn encoder(&self) -> Self::Encoder<'_> {
104        match self.inner {
105            Some(ref data) => PeginWitnessEncoder(PeginWitnessEncoderInner::Pegin(data.encoder())),
106            None => PeginWitnessEncoder(PeginWitnessEncoderInner::Empty(
107                encoding::CompactSizeEncoder::new(0),
108            )),
109        }
110    }
111}
112
113/// Decoder for the [`PeginWitness`] type.
114#[derive(Default)]
115pub struct PeginWitnessDecoder(PeginWitnessDecoderInner);
116
117#[derive(Default)]
118#[allow(clippy::large_enum_variant)]
119enum PeginWitnessDecoderInner {
120    #[default]
121    Undetermined,
122    Empty,
123    Pegin(PeginDataDecoder),
124}
125
126impl encoding::Decoder for PeginWitnessDecoder {
127    type Output = PeginWitness;
128    type Error = PeginWitnessDecoderError;
129
130    fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<encoding::DecoderStatus, Self::Error> {
131        use PeginWitnessDecoderInner as Inner;
132        loop {
133            match self.0 {
134                Inner::Undetermined => {
135                    match bytes.first().copied().map(usize::from) {
136                        None => return Ok(encoding::DecoderStatus::NeedsMore),
137                        Some(0) => {
138                            *bytes = &bytes[1..];
139                            self.0 = Inner::Empty;
140                        }
141                        Some(TOTAL_PEGIN_LENGTH) => {
142                            // don't advance `bytes`; the PeginDataDecoder will
143                            self.0 = Inner::Pegin(PeginDataDecoder::default());
144                        }
145                        Some(x) => {
146                            return Err(PeginWitnessDecoderError {
147                                field: "pegin witness",
148                                inner: PeginDataDecoderErrorInner::Length(
149                                    ExactLengthDecoderError::IncorrectLength {
150                                        expected: TOTAL_PEGIN_LENGTH,
151                                        got: x,
152                                    },
153                                ),
154                            });
155                        }
156                    }
157                }
158                Inner::Empty => return Ok(encoding::DecoderStatus::Ready),
159                Inner::Pegin(ref mut decoder) => return decoder.push_bytes(bytes),
160            }
161        }
162    }
163
164    fn end(self) -> Result<Self::Output, Self::Error> {
165        use PeginWitnessDecoderInner as Inner;
166        match self.0 {
167            Inner::Undetermined => Err(PeginWitnessDecoderError {
168                field: "pegin witness",
169                inner: PeginDataDecoderErrorInner::InsufficientLength { minimum: 1, got: 0 },
170            }),
171            Inner::Empty => Ok(PeginWitness { inner: None }),
172            Inner::Pegin(decoder) => decoder.end().map(|inner| PeginWitness { inner: Some(inner) }),
173        }
174    }
175
176    fn read_limit(&self) -> usize {
177        use PeginWitnessDecoderInner as Inner;
178        match self.0 {
179            Inner::Undetermined => 1,
180            Inner::Empty => 0,
181            Inner::Pegin(ref decoder) => decoder.read_limit(),
182        }
183    }
184}
185
186impl encoding::Decode for PeginWitness {
187    type Decoder = PeginWitnessDecoder;
188}
189
190/// Parsed data from a transaction input's pegin witness
191#[derive(Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
192pub struct PeginData {
193    /// The value, in satoshis, of the pegin
194    pub value: u64,
195    /// Asset type being pegged in
196    pub asset_id: AssetId,
197    /// Hash of genesis block of originating blockchain
198    pub genesis_hash: bitcoin::BlockHash,
199    /// The claim script that we should hash to tweak our address.
200    pub claim_script: bitcoin::ScriptBuf,
201    /// Mainchain transaction; not parsed to save time/memory since the
202    /// parsed transaction is typically not useful without auxiliary
203    /// data (e.g. knowing how to compute pegin addresses for the
204    /// sidechain).
205    pub transaction: Vec<u8>,
206    /// Merkle proof of transaction inclusion. Also not parsed.
207    pub merkle_proof: Vec<u8>,
208    /// The Bitcoin block that the pegin output appears in; scraped
209    /// from the transaction inclusion proof
210    pub referenced_block: bitcoin::BlockHash,
211}
212
213impl PeginData {
214    /// Parse the mainchain tx provided as pegin data.
215    pub fn parse_tx(&self) -> Result<bitcoin::Transaction, bitcoin::consensus::encode::Error> {
216        bitcoin::consensus::encode::deserialize(&self.transaction)
217    }
218
219    /// Parse the merkle inclusion proof provided as pegin data.
220    pub fn parse_merkle_proof(
221        &self,
222    ) -> Result<bitcoin::MerkleBlock, bitcoin::consensus::encode::Error> {
223        bitcoin::consensus::encode::deserialize(&self.merkle_proof)
224    }
225}
226
227encoding::encoder_newtype_exact! {
228    /// An encoder for the [`PeginWitness`] type.
229    #[derive(Clone, Debug)]
230    pub struct PeginDataEncoder<'e>(
231        encoding::Encoder3<
232            // top-level compactsize
233            encoding::CompactSizeEncoder,
234            encoding::Encoder6<
235                // value
236                encoding::CompactSizeEncoder,
237                encoding::ArrayEncoder<8>,
238                // asset ID
239                encoding::CompactSizeEncoder,
240                encoding::ArrayRefEncoder<'e, 32>,
241                // genesis hash
242                encoding::CompactSizeEncoder,
243                GenesisHashEncoder<'e>,
244            >,
245            encoding::Encoder3<
246                // claim script
247                bitcoin::blockdata::script::ScriptEncoder<'e>,
248                // transaction
249                encoding::PrefixedBytesEncoder<'e>,
250                // merkle proof
251                encoding::PrefixedBytesEncoder<'e>,
252
253            >,
254        >
255    );
256}
257
258impl encoding::Encode for PeginData {
259    type Encoder<'e> = PeginDataEncoder<'e>;
260
261    fn encoder(&self) -> Self::Encoder<'_> {
262        PeginDataEncoder::new(encoding::Encoder3::new(
263            encoding::CompactSizeEncoder::new(TOTAL_PEGIN_LENGTH),
264            encoding::Encoder6::new(
265                encoding::CompactSizeEncoder::new(8),
266                encoding::ArrayEncoder::without_length_prefix(self.value.to_le_bytes()),
267                encoding::CompactSizeEncoder::new(32),
268                encoding::ArrayRefEncoder::without_length_prefix(self.asset_id.as_byte_array()),
269                encoding::CompactSizeEncoder::new(32),
270                self.genesis_hash.encoder(),
271            ),
272            encoding::Encoder3::new(
273                self.claim_script.encoder(),
274                encoding::PrefixedBytesEncoder::new(&self.transaction),
275                encoding::PrefixedBytesEncoder::new(&self.merkle_proof),
276            ),
277        ))
278    }
279}
280
281struct ExactLengthDecoder {
282    target: usize,
283    inner: encoding::CompactSizeDecoder,
284}
285
286impl ExactLengthDecoder {
287    fn new(target: usize) -> Self { Self { target, inner: encoding::CompactSizeDecoder::new() } }
288}
289
290#[derive(Clone, PartialEq, Eq, Debug)]
291enum ExactLengthDecoderError {
292    IncorrectLength { expected: usize, got: usize },
293    InvalidCompactSize(encoding::CompactSizeDecoderError),
294}
295
296impl fmt::Display for ExactLengthDecoderError {
297    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298        match *self {
299            Self::IncorrectLength { expected, got } => {
300                write!(f, "expected length {expected} but got {got}")
301            }
302            Self::InvalidCompactSize(_) => f.write_str("failed to decode compact size"),
303        }
304    }
305}
306
307impl std::error::Error for ExactLengthDecoderError {
308    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
309        match *self {
310            Self::IncorrectLength { .. } => None,
311            Self::InvalidCompactSize(ref error) => Some(error),
312        }
313    }
314}
315
316impl encoding::Decoder for ExactLengthDecoder {
317    type Output = usize;
318    type Error = ExactLengthDecoderError;
319
320    fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<encoding::DecoderStatus, Self::Error> {
321        self.inner.push_bytes(bytes).map_err(ExactLengthDecoderError::InvalidCompactSize)
322    }
323
324    fn end(self) -> Result<Self::Output, Self::Error> {
325        let expected = self.target;
326        let got = self.inner.end().map_err(ExactLengthDecoderError::InvalidCompactSize)?;
327
328        if got == self.target {
329            Ok(got)
330        } else {
331            Err(ExactLengthDecoderError::IncorrectLength { expected, got })
332        }
333    }
334
335    fn read_limit(&self) -> usize { self.inner.read_limit() }
336}
337
338/// A decoder for the [`PeginData`] type.
339#[allow(clippy::type_complexity)]
340pub struct PeginDataDecoder {
341    inner: encoding::Decoder3<
342        // top-level compactsize
343        ExactLengthDecoder,
344        encoding::Decoder6<
345            // value
346            ExactLengthDecoder,
347            encoding::ArrayDecoder<8>,
348            // asset ID
349            ExactLengthDecoder,
350            crate::AssetIdDecoder,
351            // genesis hash
352            ExactLengthDecoder,
353            GenesisHashDecoder,
354        >,
355        encoding::Decoder3<
356            // claim script
357            bitcoin::blockdata::script::ScriptBufDecoder,
358            // transaction
359            encoding::ByteVecDecoder,
360            // merkle proof
361            encoding::ByteVecDecoder,
362        >,
363    >,
364}
365
366impl Default for PeginDataDecoder {
367    fn default() -> Self {
368        Self {
369            inner: encoding::Decoder3::new(
370                ExactLengthDecoder::new(TOTAL_PEGIN_LENGTH),
371                encoding::Decoder6::new(
372                    ExactLengthDecoder::new(8),
373                    encoding::ArrayDecoder::new(),
374                    ExactLengthDecoder::new(32),
375                    crate::AssetIdDecoder::default(),
376                    ExactLengthDecoder::new(32),
377                    GenesisHashDecoder::new(),
378                ),
379                encoding::Decoder3::new(
380                    bitcoin::blockdata::script::ScriptBufDecoder::new(),
381                    encoding::ByteVecDecoder::new(),
382                    encoding::ByteVecDecoder::new(),
383                ),
384            ),
385        }
386    }
387}
388
389#[derive(Clone, PartialEq, Eq, Debug)]
390enum PeginDataDecoderErrorInner {
391    AssetId(crate::AssetIdDecoderError),
392    ClaimScript(bitcoin::blockdata::script::ScriptBufDecoderError),
393    GenesisHash(GenesisHashDecoderError),
394    Length(ExactLengthDecoderError),
395    InsufficientLength { minimum: usize, got: usize },
396    Eof(encoding::UnexpectedEofError),
397    ByteVec(encoding::ByteVecDecoderError),
398}
399
400/// Error type for the decoding of [`PeginData`] or [`PeginWitness`].
401#[derive(Clone, PartialEq, Eq, Debug)]
402pub struct PeginWitnessDecoderError {
403    field: &'static str,
404    inner: PeginDataDecoderErrorInner,
405}
406
407impl fmt::Display for PeginWitnessDecoderError {
408    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409        if self.field == "pegin witness" {
410            f.write_str("error decoding pegin witness")?;
411        } else {
412            write!(f, "error decoding pegin witness field {}", self.field)?;
413        }
414        if let PeginDataDecoderErrorInner::InsufficientLength { minimum, got } = self.inner {
415            write!(f, ": needed at least {minimum} bytes, got {got}")?;
416        }
417        Ok(())
418    }
419}
420
421impl std::error::Error for PeginWitnessDecoderError {
422    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
423        use PeginDataDecoderErrorInner as Inner;
424        match self.inner {
425            Inner::AssetId(ref e) => Some(e),
426            Inner::ClaimScript(ref e) => Some(e),
427            Inner::GenesisHash(ref e) => Some(e),
428            Inner::Length(ref e) => Some(e),
429            Inner::InsufficientLength { .. } => None,
430            Inner::Eof(ref e) => Some(e),
431            Inner::ByteVec(ref e) => Some(e),
432        }
433    }
434}
435
436impl PeginWitnessDecoderError {
437    /// Private error conversion function.
438    #[allow(clippy::type_complexity)]
439    fn from(
440        inner: encoding::Decoder3Error<
441            ExactLengthDecoderError,
442            encoding::Decoder6Error<
443                ExactLengthDecoderError,
444                UnexpectedEofError,
445                ExactLengthDecoderError,
446                crate::AssetIdDecoderError,
447                ExactLengthDecoderError,
448                GenesisHashDecoderError,
449            >,
450            encoding::Decoder3Error<
451                bitcoin::blockdata::script::ScriptBufDecoderError,
452                encoding::ByteVecDecoderError,
453                encoding::ByteVecDecoderError,
454            >,
455        >,
456    ) -> Self {
457        use encoding::{Decoder3Error as Dec3Err, Decoder6Error as Dec6Err};
458
459        match inner {
460            Dec3Err::First(error) =>
461                Self { field: "pegin witness", inner: PeginDataDecoderErrorInner::Length(error) },
462            Dec3Err::Second(Dec6Err::First(error)) =>
463                Self { field: "value", inner: PeginDataDecoderErrorInner::Length(error) },
464            Dec3Err::Second(Dec6Err::Second(error)) =>
465                Self { field: "value", inner: PeginDataDecoderErrorInner::Eof(error) },
466            Dec3Err::Second(Dec6Err::Third(error)) =>
467                Self { field: "asset ID", inner: PeginDataDecoderErrorInner::Length(error) },
468            Dec3Err::Second(Dec6Err::Fourth(error)) =>
469                Self { field: "asset ID", inner: PeginDataDecoderErrorInner::AssetId(error) },
470            Dec3Err::Second(Dec6Err::Fifth(error)) =>
471                Self { field: "genesis hash", inner: PeginDataDecoderErrorInner::Length(error) },
472            Dec3Err::Second(Dec6Err::Sixth(error)) => Self {
473                field: "genesis hash",
474                inner: PeginDataDecoderErrorInner::GenesisHash(error),
475            },
476            Dec3Err::Third(Dec3Err::First(error)) => Self {
477                field: "claim script",
478                inner: PeginDataDecoderErrorInner::ClaimScript(error),
479            },
480            Dec3Err::Third(Dec3Err::Second(error)) =>
481                Self { field: "transaction", inner: PeginDataDecoderErrorInner::ByteVec(error) },
482            Dec3Err::Third(Dec3Err::Third(error)) =>
483                Self { field: "merkle proof", inner: PeginDataDecoderErrorInner::ByteVec(error) },
484        }
485    }
486}
487
488impl encoding::Decoder for PeginDataDecoder {
489    type Output = PeginData;
490    type Error = PeginWitnessDecoderError;
491
492    fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<encoding::DecoderStatus, Self::Error> {
493        self.inner.push_bytes(bytes).map_err(PeginWitnessDecoderError::from)
494    }
495
496    fn end(self) -> Result<Self::Output, Self::Error> {
497        use internals::slice::SliceExt;
498
499        let (
500            _,
501            (_, value, _, asset_id, _, genesis_hash),
502            (claim_script, transaction, merkle_proof),
503        ) = self.inner.end().map_err(PeginWitnessDecoderError::from)?;
504
505        let Some((block_header, _)) = SliceExt::split_first_chunk::<80>(merkle_proof.as_slice())
506        else {
507            return Err(PeginWitnessDecoderError {
508                field: "merkle proof",
509                inner: PeginDataDecoderErrorInner::InsufficientLength {
510                    minimum: 80,
511                    got: merkle_proof.len(),
512                },
513            });
514        };
515        let referenced_block = bitcoin::BlockHash::hash(block_header);
516
517        Ok(PeginData {
518            value: u64::from_le_bytes(value),
519            asset_id,
520            genesis_hash,
521            claim_script,
522            transaction,
523            merkle_proof,
524            referenced_block,
525        })
526    }
527
528    fn read_limit(&self) -> usize { self.inner.read_limit() }
529}
530
531impl encoding::Decode for PeginData {
532    type Decoder = PeginDataDecoder;
533}
534
535impl encode::Encodable for PeginData {
536    fn consensus_encode<W: io::Write>(&self, e: W) -> Result<usize, encode::Error> {
537        let mut counter = encode::ByteCounter::new(e);
538        crate::encoding::encode_to_writer(self, &mut counter)?;
539        Ok(counter.into_count())
540    }
541}
542
543impl encode::Decodable for PeginWitness {
544    fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> {
545        match encoding::decode_from_read_unbuffered(d) {
546            Ok(res) => Ok(res),
547            Err(encoding::ReadError::Io(e)) => Err(encode::Error::Io(e)),
548            Err(encoding::ReadError::Decode(e)) => Err(encode::Error::PeginWitness(e)),
549        }
550    }
551}
552
553impl encode::Encodable for PeginWitness {
554    fn consensus_encode<W: io::Write>(&self, e: W) -> Result<usize, encode::Error> {
555        match self.inner {
556            Some(ref wit) => wit.consensus_encode(e),
557            None => 0u8.consensus_encode(e),
558        }
559    }
560}