Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop implementing elements::Encodable with bitcoin::Encodable #212

Merged
merged 2 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ use std::io;
use crate::dynafed;
use crate::hashes::{Hash, sha256};
use crate::Transaction;
use crate::encode::{self, Encodable, Decodable, serialize};
use crate::{BlockHash, Script, TxMerkleNode, VarInt};
use crate::encode::{self, serialize, Decodable, Encodable, VarInt};
use crate::{BlockHash, Script, TxMerkleNode};

/// Data related to block signatures
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
Expand Down
180 changes: 136 additions & 44 deletions src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use std::io::Cursor;
use std::{error, fmt, io, mem};

use bitcoin::consensus::encode as btcenc;
use bitcoin::ScriptBuf;
use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak};

use crate::hashes::{sha256, Hash};
Expand All @@ -27,9 +27,6 @@ use crate::transaction::{Transaction, TxIn, TxOut};

pub use bitcoin::{self, consensus::encode::MAX_VEC_SIZE};

// Use the ReadExt/WriteExt traits as is from upstream
pub use bitcoin::consensus::encode::{ReadExt, WriteExt};

use crate::taproot::TapLeafHash;

/// Encoding error
Expand All @@ -38,7 +35,7 @@ pub enum Error {
/// And I/O error
Io(io::Error),
/// A Bitcoin encoding error.
Bitcoin(btcenc::Error),
Bitcoin(bitcoin::consensus::encode::Error),
/// Tried to allocate an oversized vector
OversizedVectorAllocation {
/// The capacity requested
Expand All @@ -62,6 +59,8 @@ pub enum Error {
HexError(crate::hex::Error),
/// Got a time-based locktime when expecting a height-based one, or vice-versa
BadLockTime(crate::LockTime),
/// VarInt was encoded in a non-minimal way.
NonMinimalVarInt,
}

impl fmt::Display for Error {
Expand All @@ -87,23 +86,22 @@ impl fmt::Display for Error {
Error::PsetError(ref e) => write!(f, "Pset Error: {}", e),
Error::HexError(ref e) => write!(f, "Hex error {}", e),
Error::BadLockTime(ref lt) => write!(f, "Invalid locktime {}", lt),
Error::NonMinimalVarInt => write!(f, "non-minimal varint"),
}
}
}

impl error::Error for Error {
fn cause(&self) -> Option<&dyn error::Error> {
match *self {
Error::Bitcoin(ref e) => Some(e),
Error::Secp256k1zkp(ref e) => Some(e),
_ => None,
}
}
}

#[doc(hidden)]
impl From<btcenc::Error> for Error {
fn from(e: btcenc::Error) -> Error {
impl From<bitcoin::consensus::encode::Error> for Error {
fn from(e: bitcoin::consensus::encode::Error) -> Error {
Error::Bitcoin(e)
}
}
Expand Down Expand Up @@ -206,46 +204,15 @@ impl Decodable for sha256::Midstate {
}
}

pub(crate) fn consensus_encode_with_size<S: io::Write>(
pub(crate) fn consensus_encode_with_size<S: crate::WriteExt>(
data: &[u8],
mut s: S,
) -> Result<usize, Error> {
let vi_len = bitcoin::VarInt(data.len() as u64).consensus_encode(&mut s)?;
let vi_len = VarInt(data.len() as u64).consensus_encode(&mut s)?;
s.emit_slice(data)?;
Ok(vi_len + data.len())
}

/// Implement Elements encodable traits for Bitcoin encodable types.
macro_rules! impl_upstream {
($type: ty) => {
impl Encodable for $type {
fn consensus_encode<W: io::Write>(&self, mut e: W) -> Result<usize, Error> {
Ok(btcenc::Encodable::consensus_encode(self, &mut e)?)
}
}

impl Decodable for $type {
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
Ok(btcenc::Decodable::consensus_decode(&mut d)?)
}
}
};
}
impl_upstream!(u8);
impl_upstream!(u32);
impl_upstream!(u64);
impl_upstream!([u8; 4]);
impl_upstream!([u8; 32]);
impl_upstream!(Box<[u8]>);
impl_upstream!([u8; 33]);
impl_upstream!(Vec<u8>);
impl_upstream!(Vec<Vec<u8>>);
impl_upstream!(btcenc::VarInt);
impl_upstream!(bitcoin::Transaction);
impl_upstream!(bitcoin::BlockHash);
impl_upstream!(bitcoin::ScriptBuf);
impl_upstream!(crate::hashes::sha256d::Hash);

// Specific locktime types (which appear in PSET/PSBT2 but not in rust-bitcoin PSBT)
impl Encodable for crate::locktime::Height {
fn consensus_encode<S: io::Write>(&self, s: S) -> Result<usize, Error> {
Expand Down Expand Up @@ -275,14 +242,85 @@ impl Decodable for crate::locktime::Time {
}
}

/// A variable sized integer.
pub struct VarInt(pub u64);
impl Encodable for VarInt {
fn consensus_encode<W: crate::WriteExt>(&self, mut e: W) -> Result<usize, Error> {
Ok(e.emit_varint(self.0)?)
}
}
impl Decodable for VarInt {
fn consensus_decode<D: crate::ReadExt>(mut d: D) -> Result<Self, Error> {
Ok(VarInt(d.read_varint()?))
}
}
impl VarInt {
/// returns the byte size used if this var int is serialized
pub fn size(&self) -> usize {
match self.0 {
0..=0xFC => 1,
0xFD..=0xFFFF => 3,
0x10000..=0xFFFFFFFF => 5,
_ => 9,
}
}
}

// Primitive types
macro_rules! impl_int {
($ty:ident, $meth_dec:ident, $meth_enc:ident) => {
impl Encodable for $ty {
fn consensus_encode<W: crate::WriteExt>(&self, mut w: W) -> Result<usize, Error> {
w.$meth_enc(*self)?;
Ok(mem::size_of::<$ty>())
}
}
impl Decodable for $ty {
fn consensus_decode<R: crate::ReadExt>(mut r: R) -> Result<Self, Error> {
crate::ReadExt::$meth_dec(&mut r)
}
}
};
}

impl_int!(u8, read_u8, emit_u8);
impl_int!(u16, read_u16, emit_u16);
impl_int!(u32, read_u32, emit_u32);
impl_int!(u64, read_u64, emit_u64);

impl Encodable for bitcoin::ScriptBuf {
fn consensus_encode<W: io::Write>(&self, w: W) -> Result<usize, Error> {
consensus_encode_with_size(self.as_script().as_bytes(), w)
}
}
impl Decodable for bitcoin::ScriptBuf {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
let bytes = Vec::<u8>::consensus_decode(d)?;
Ok(ScriptBuf::from_bytes(bytes))
}
}

impl Encodable for bitcoin::hashes::sha256d::Hash {
fn consensus_encode<W: io::Write>(&self, mut w: W) -> Result<usize, Error> {
self.as_byte_array().consensus_encode(&mut w)
}
}
impl Decodable for bitcoin::hashes::sha256d::Hash {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
Ok(Self::from_byte_array(
<<Self as Hash>::Bytes>::consensus_decode(d)?,
))
}
}

// Vectors
macro_rules! impl_vec {
($type: ty) => {
impl Encodable for Vec<$type> {
#[inline]
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, Error> {
let mut len = 0;
len += btcenc::VarInt(self.len() as u64).consensus_encode(&mut s)?;
len += VarInt(self.len() as u64).consensus_encode(&mut s)?;
for c in self.iter() {
len += c.consensus_encode(&mut s)?;
}
Expand All @@ -293,7 +331,7 @@ macro_rules! impl_vec {
impl Decodable for Vec<$type> {
#[inline]
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
let len = btcenc::VarInt::consensus_decode(&mut d)?.0;
let len = VarInt::consensus_decode(&mut d)?.0;
let byte_size = (len as usize)
.checked_mul(mem::size_of::<$type>())
.ok_or(self::Error::ParseFailed("Invalid length"))?;
Expand All @@ -316,6 +354,60 @@ impl_vec!(TxIn);
impl_vec!(TxOut);
impl_vec!(Transaction);
impl_vec!(TapLeafHash);
impl_vec!(Vec<u8>); // Vec<Vec<u8>>

macro_rules! impl_array {
( $size:literal ) => {
impl Encodable for [u8; $size] {
#[inline]
fn consensus_encode<W: crate::WriteExt>(
&self,
mut w: W,
) -> core::result::Result<usize, Error> {
w.emit_slice(&self[..])?;
Ok($size)
}
}

impl Decodable for [u8; $size] {
#[inline]
fn consensus_decode<R: crate::ReadExt>(mut r: R) -> core::result::Result<Self, Error> {
let mut ret = [0; $size];
r.read_slice(&mut ret)?;
Ok(ret)
}
}
};
}
impl_array!(4);
impl_array!(32);
impl_array!(33);

impl Encodable for Box<[u8]> {
fn consensus_encode<W: io::Write>(&self, mut w: W) -> Result<usize, Error> {
consensus_encode_with_size(&self[..], &mut w)
}
}
impl Decodable for Box<[u8]> {
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
let v = Vec::<u8>::consensus_decode(d)?;
Ok(v.into())
}
}

impl Encodable for Vec<u8> {
fn consensus_encode<W: io::Write>(&self, mut w: W) -> Result<usize, Error> {
consensus_encode_with_size(&self[..], &mut w)
}
}
impl Decodable for Vec<u8> {
fn consensus_decode<D: crate::ReadExt>(mut d: D) -> Result<Self, Error> {
let s = VarInt::consensus_decode(&mut d)?.0 as usize;
let mut v = vec![0; s];
d.read_slice(&mut v)?;
Ok(v)
}
}

macro_rules! impl_box_option {
($type: ty) => {
Expand Down
Loading
Loading