use core::fmt;
use std::io;
use bitcoin::block::BlockHashDecoder as GenesisHashDecoder;
use bitcoin::block::{
BlockHashDecoderError as GenesisHashDecoderError, BlockHashEncoder as GenesisHashEncoder,
};
use bitcoin::hashes::Hash as _;
use hashes::encoding::UnexpectedEofError;
use crate::{encode, encoding, AssetId};
const TOTAL_PEGIN_LENGTH: usize = 6;
#[derive(Default, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
pub struct PeginWitness {
inner: Option<PeginData>,
}
impl PeginWitness {
pub const EMPTY: Self = Self { inner: None };
pub fn new(data: PeginData) -> Self { Self { inner: Some(data) } }
pub fn data(&self) -> Option<&PeginData> { self.inner.as_ref() }
pub fn into_data(self) -> Option<PeginData> { self.inner }
pub fn is_empty(&self) -> bool { self.inner.is_none() }
pub fn len(&self) -> usize {
if self.inner.is_some() {
TOTAL_PEGIN_LENGTH
} else {
0
}
}
pub fn encoded_size(&self) -> usize {
use encoding::{Encode as _, ExactSizeEncoder as _};
match self.inner {
Some(ref data) => data.encoder().len(),
None => 1,
}
}
}
#[derive(Clone, Debug)]
pub struct PeginWitnessEncoder<'e>(PeginWitnessEncoderInner<'e>);
#[derive(Clone, Debug)]
#[allow(clippy::large_enum_variant)]
enum PeginWitnessEncoderInner<'e> {
Pegin(PeginDataEncoder<'e>),
Empty(encoding::CompactSizeEncoder),
}
impl encoding::Encoder for PeginWitnessEncoder<'_> {
fn current_chunk(&self) -> &[u8] {
use PeginWitnessEncoderInner as Inner;
match self.0 {
Inner::Pegin(ref enc) => enc.current_chunk(),
Inner::Empty(ref enc) => enc.current_chunk(),
}
}
fn advance(&mut self) -> encoding::EncoderStatus {
use PeginWitnessEncoderInner as Inner;
match self.0 {
Inner::Pegin(ref mut enc) => enc.advance(),
Inner::Empty(ref mut enc) => enc.advance(),
}
}
}
impl encoding::ExactSizeEncoder for PeginWitnessEncoder<'_> {
fn len(&self) -> usize {
use PeginWitnessEncoderInner as Inner;
match self.0 {
Inner::Pegin(ref enc) => enc.len(),
Inner::Empty(ref enc) => enc.len(),
}
}
}
impl encoding::Encode for PeginWitness {
type Encoder<'e> = PeginWitnessEncoder<'e>;
fn encoder(&self) -> Self::Encoder<'_> {
match self.inner {
Some(ref data) => PeginWitnessEncoder(PeginWitnessEncoderInner::Pegin(data.encoder())),
None => PeginWitnessEncoder(PeginWitnessEncoderInner::Empty(
encoding::CompactSizeEncoder::new(0),
)),
}
}
}
#[derive(Default)]
pub struct PeginWitnessDecoder(PeginWitnessDecoderInner);
#[derive(Default)]
#[allow(clippy::large_enum_variant)]
enum PeginWitnessDecoderInner {
#[default]
Undetermined,
Empty,
Pegin(PeginDataDecoder),
}
impl encoding::Decoder for PeginWitnessDecoder {
type Output = PeginWitness;
type Error = PeginWitnessDecoderError;
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<encoding::DecoderStatus, Self::Error> {
use PeginWitnessDecoderInner as Inner;
loop {
match self.0 {
Inner::Undetermined => {
match bytes.first().copied().map(usize::from) {
None => return Ok(encoding::DecoderStatus::NeedsMore),
Some(0) => {
*bytes = &bytes[1..];
self.0 = Inner::Empty;
}
Some(TOTAL_PEGIN_LENGTH) => {
self.0 = Inner::Pegin(PeginDataDecoder::default());
}
Some(x) => {
return Err(PeginWitnessDecoderError {
field: "pegin witness",
inner: PeginDataDecoderErrorInner::Length(
ExactLengthDecoderError::IncorrectLength {
expected: TOTAL_PEGIN_LENGTH,
got: x,
},
),
});
}
}
}
Inner::Empty => return Ok(encoding::DecoderStatus::Ready),
Inner::Pegin(ref mut decoder) => return decoder.push_bytes(bytes),
}
}
}
fn end(self) -> Result<Self::Output, Self::Error> {
use PeginWitnessDecoderInner as Inner;
match self.0 {
Inner::Undetermined => Err(PeginWitnessDecoderError {
field: "pegin witness",
inner: PeginDataDecoderErrorInner::InsufficientLength { minimum: 1, got: 0 },
}),
Inner::Empty => Ok(PeginWitness { inner: None }),
Inner::Pegin(decoder) => decoder.end().map(|inner| PeginWitness { inner: Some(inner) }),
}
}
fn read_limit(&self) -> usize {
use PeginWitnessDecoderInner as Inner;
match self.0 {
Inner::Undetermined => 1,
Inner::Empty => 0,
Inner::Pegin(ref decoder) => decoder.read_limit(),
}
}
}
impl encoding::Decode for PeginWitness {
type Decoder = PeginWitnessDecoder;
}
#[derive(Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
pub struct PeginData {
pub value: u64,
pub asset_id: AssetId,
pub genesis_hash: bitcoin::BlockHash,
pub claim_script: bitcoin::ScriptBuf,
pub transaction: Vec<u8>,
pub merkle_proof: Vec<u8>,
pub referenced_block: bitcoin::BlockHash,
}
impl PeginData {
pub fn parse_tx(&self) -> Result<bitcoin::Transaction, bitcoin::consensus::encode::Error> {
bitcoin::consensus::encode::deserialize(&self.transaction)
}
pub fn parse_merkle_proof(
&self,
) -> Result<bitcoin::MerkleBlock, bitcoin::consensus::encode::Error> {
bitcoin::consensus::encode::deserialize(&self.merkle_proof)
}
}
encoding::encoder_newtype_exact! {
#[derive(Clone, Debug)]
pub struct PeginDataEncoder<'e>(
encoding::Encoder3<
encoding::CompactSizeEncoder,
encoding::Encoder6<
encoding::CompactSizeEncoder,
encoding::ArrayEncoder<8>,
encoding::CompactSizeEncoder,
encoding::ArrayRefEncoder<'e, 32>,
encoding::CompactSizeEncoder,
GenesisHashEncoder<'e>,
>,
encoding::Encoder3<
bitcoin::blockdata::script::ScriptEncoder<'e>,
encoding::PrefixedBytesEncoder<'e>,
encoding::PrefixedBytesEncoder<'e>,
>,
>
);
}
impl encoding::Encode for PeginData {
type Encoder<'e> = PeginDataEncoder<'e>;
fn encoder(&self) -> Self::Encoder<'_> {
PeginDataEncoder::new(encoding::Encoder3::new(
encoding::CompactSizeEncoder::new(TOTAL_PEGIN_LENGTH),
encoding::Encoder6::new(
encoding::CompactSizeEncoder::new(8),
encoding::ArrayEncoder::without_length_prefix(self.value.to_le_bytes()),
encoding::CompactSizeEncoder::new(32),
encoding::ArrayRefEncoder::without_length_prefix(self.asset_id.as_byte_array()),
encoding::CompactSizeEncoder::new(32),
self.genesis_hash.encoder(),
),
encoding::Encoder3::new(
self.claim_script.encoder(),
encoding::PrefixedBytesEncoder::new(&self.transaction),
encoding::PrefixedBytesEncoder::new(&self.merkle_proof),
),
))
}
}
struct ExactLengthDecoder {
target: usize,
inner: encoding::CompactSizeDecoder,
}
impl ExactLengthDecoder {
fn new(target: usize) -> Self { Self { target, inner: encoding::CompactSizeDecoder::new() } }
}
#[derive(Clone, PartialEq, Eq, Debug)]
enum ExactLengthDecoderError {
IncorrectLength { expected: usize, got: usize },
InvalidCompactSize(encoding::CompactSizeDecoderError),
}
impl fmt::Display for ExactLengthDecoderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::IncorrectLength { expected, got } => {
write!(f, "expected length {expected} but got {got}")
}
Self::InvalidCompactSize(_) => f.write_str("failed to decode compact size"),
}
}
}
impl std::error::Error for ExactLengthDecoderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Self::IncorrectLength { .. } => None,
Self::InvalidCompactSize(ref error) => Some(error),
}
}
}
impl encoding::Decoder for ExactLengthDecoder {
type Output = usize;
type Error = ExactLengthDecoderError;
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<encoding::DecoderStatus, Self::Error> {
self.inner.push_bytes(bytes).map_err(ExactLengthDecoderError::InvalidCompactSize)
}
fn end(self) -> Result<Self::Output, Self::Error> {
let expected = self.target;
let got = self.inner.end().map_err(ExactLengthDecoderError::InvalidCompactSize)?;
if got == self.target {
Ok(got)
} else {
Err(ExactLengthDecoderError::IncorrectLength { expected, got })
}
}
fn read_limit(&self) -> usize { self.inner.read_limit() }
}
#[allow(clippy::type_complexity)]
pub struct PeginDataDecoder {
inner: encoding::Decoder3<
ExactLengthDecoder,
encoding::Decoder6<
ExactLengthDecoder,
encoding::ArrayDecoder<8>,
ExactLengthDecoder,
crate::AssetIdDecoder,
ExactLengthDecoder,
GenesisHashDecoder,
>,
encoding::Decoder3<
bitcoin::blockdata::script::ScriptBufDecoder,
encoding::ByteVecDecoder,
encoding::ByteVecDecoder,
>,
>,
}
impl Default for PeginDataDecoder {
fn default() -> Self {
Self {
inner: encoding::Decoder3::new(
ExactLengthDecoder::new(TOTAL_PEGIN_LENGTH),
encoding::Decoder6::new(
ExactLengthDecoder::new(8),
encoding::ArrayDecoder::new(),
ExactLengthDecoder::new(32),
crate::AssetIdDecoder::default(),
ExactLengthDecoder::new(32),
GenesisHashDecoder::new(),
),
encoding::Decoder3::new(
bitcoin::blockdata::script::ScriptBufDecoder::new(),
encoding::ByteVecDecoder::new(),
encoding::ByteVecDecoder::new(),
),
),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
enum PeginDataDecoderErrorInner {
AssetId(crate::AssetIdDecoderError),
ClaimScript(bitcoin::blockdata::script::ScriptBufDecoderError),
GenesisHash(GenesisHashDecoderError),
Length(ExactLengthDecoderError),
InsufficientLength { minimum: usize, got: usize },
Eof(encoding::UnexpectedEofError),
ByteVec(encoding::ByteVecDecoderError),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct PeginWitnessDecoderError {
field: &'static str,
inner: PeginDataDecoderErrorInner,
}
impl fmt::Display for PeginWitnessDecoderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.field == "pegin witness" {
f.write_str("error decoding pegin witness")?;
} else {
write!(f, "error decoding pegin witness field {}", self.field)?;
}
if let PeginDataDecoderErrorInner::InsufficientLength { minimum, got } = self.inner {
write!(f, ": needed at least {minimum} bytes, got {got}")?;
}
Ok(())
}
}
impl std::error::Error for PeginWitnessDecoderError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use PeginDataDecoderErrorInner as Inner;
match self.inner {
Inner::AssetId(ref e) => Some(e),
Inner::ClaimScript(ref e) => Some(e),
Inner::GenesisHash(ref e) => Some(e),
Inner::Length(ref e) => Some(e),
Inner::InsufficientLength { .. } => None,
Inner::Eof(ref e) => Some(e),
Inner::ByteVec(ref e) => Some(e),
}
}
}
impl PeginWitnessDecoderError {
#[allow(clippy::type_complexity)]
fn from(
inner: encoding::Decoder3Error<
ExactLengthDecoderError,
encoding::Decoder6Error<
ExactLengthDecoderError,
UnexpectedEofError,
ExactLengthDecoderError,
crate::AssetIdDecoderError,
ExactLengthDecoderError,
GenesisHashDecoderError,
>,
encoding::Decoder3Error<
bitcoin::blockdata::script::ScriptBufDecoderError,
encoding::ByteVecDecoderError,
encoding::ByteVecDecoderError,
>,
>,
) -> Self {
use encoding::{Decoder3Error as Dec3Err, Decoder6Error as Dec6Err};
match inner {
Dec3Err::First(error) =>
Self { field: "pegin witness", inner: PeginDataDecoderErrorInner::Length(error) },
Dec3Err::Second(Dec6Err::First(error)) =>
Self { field: "value", inner: PeginDataDecoderErrorInner::Length(error) },
Dec3Err::Second(Dec6Err::Second(error)) =>
Self { field: "value", inner: PeginDataDecoderErrorInner::Eof(error) },
Dec3Err::Second(Dec6Err::Third(error)) =>
Self { field: "asset ID", inner: PeginDataDecoderErrorInner::Length(error) },
Dec3Err::Second(Dec6Err::Fourth(error)) =>
Self { field: "asset ID", inner: PeginDataDecoderErrorInner::AssetId(error) },
Dec3Err::Second(Dec6Err::Fifth(error)) =>
Self { field: "genesis hash", inner: PeginDataDecoderErrorInner::Length(error) },
Dec3Err::Second(Dec6Err::Sixth(error)) => Self {
field: "genesis hash",
inner: PeginDataDecoderErrorInner::GenesisHash(error),
},
Dec3Err::Third(Dec3Err::First(error)) => Self {
field: "claim script",
inner: PeginDataDecoderErrorInner::ClaimScript(error),
},
Dec3Err::Third(Dec3Err::Second(error)) =>
Self { field: "transaction", inner: PeginDataDecoderErrorInner::ByteVec(error) },
Dec3Err::Third(Dec3Err::Third(error)) =>
Self { field: "merkle proof", inner: PeginDataDecoderErrorInner::ByteVec(error) },
}
}
}
impl encoding::Decoder for PeginDataDecoder {
type Output = PeginData;
type Error = PeginWitnessDecoderError;
fn push_bytes(&mut self, bytes: &mut &[u8]) -> Result<encoding::DecoderStatus, Self::Error> {
self.inner.push_bytes(bytes).map_err(PeginWitnessDecoderError::from)
}
fn end(self) -> Result<Self::Output, Self::Error> {
use internals::slice::SliceExt;
let (
_,
(_, value, _, asset_id, _, genesis_hash),
(claim_script, transaction, merkle_proof),
) = self.inner.end().map_err(PeginWitnessDecoderError::from)?;
let Some((block_header, _)) = SliceExt::split_first_chunk::<80>(merkle_proof.as_slice())
else {
return Err(PeginWitnessDecoderError {
field: "merkle proof",
inner: PeginDataDecoderErrorInner::InsufficientLength {
minimum: 80,
got: merkle_proof.len(),
},
});
};
let referenced_block = bitcoin::BlockHash::hash(block_header);
Ok(PeginData {
value: u64::from_le_bytes(value),
asset_id,
genesis_hash,
claim_script,
transaction,
merkle_proof,
referenced_block,
})
}
fn read_limit(&self) -> usize { self.inner.read_limit() }
}
impl encoding::Decode for PeginData {
type Decoder = PeginDataDecoder;
}
impl encode::Encodable for PeginData {
fn consensus_encode<W: io::Write>(&self, e: W) -> Result<usize, encode::Error> {
let mut counter = encode::ByteCounter::new(e);
crate::encoding::encode_to_writer(self, &mut counter)?;
Ok(counter.into_count())
}
}
impl encode::Decodable for PeginWitness {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, encode::Error> {
match encoding::decode_from_read_unbuffered(d) {
Ok(res) => Ok(res),
Err(encoding::ReadError::Io(e)) => Err(encode::Error::Io(e)),
Err(encoding::ReadError::Decode(e)) => Err(encode::Error::PeginWitness(e)),
}
}
}
impl encode::Encodable for PeginWitness {
fn consensus_encode<W: io::Write>(&self, e: W) -> Result<usize, encode::Error> {
match self.inner {
Some(ref wit) => wit.consensus_encode(e),
None => 0u8.consensus_encode(e),
}
}
}