Skip to content

Commit

Permalink
Merge pull request #101 from BP-WG/signer
Browse files Browse the repository at this point in the history
Signing abilities: adding necessary types and logic to consensus
  • Loading branch information
dr-orlovsky authored Jul 17, 2024
2 parents 12931d1 + 23bc44c commit 1989b57
Show file tree
Hide file tree
Showing 10 changed files with 858 additions and 39 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ single_use_seals = "0.11.0-beta.6"
bp-consensus = { version = "0.11.0-beta.6", path = "consensus" }
bp-dbc = { version = "0.11.0-beta.6", path = "./dbc" }
bp-seals = { version = "0.11.0-beta.6", path = "./seals" }
secp256k1 = { version = "0.29.0", features = ["global-context"] }
secp256k1 = { version = "0.29.0", features = ["global-context", "rand-std"] }
serde_crate = { package = "serde", version = "1", features = ["derive"] }

[package]
Expand Down
67 changes: 56 additions & 11 deletions consensus/src/coding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ use amplify::confinement::{Confined, MediumBlob, SmallBlob, TinyBlob, U32};
use amplify::{confinement, ByteArray, Bytes32, IoError, Wrapper};

use crate::{
BlockHash, BlockHeader, BlockMerkleRoot, ControlBlock, InternalPk, InvalidLeafVer, LeafVer,
LockTime, Outpoint, Parity, RedeemScript, Sats, ScriptBytes, ScriptPubkey, SeqNo, SigScript,
TapBranchHash, TapMerklePath, TapScript, Tx, TxIn, TxOut, TxVer, Txid, Vout, Witness,
WitnessScript, LIB_NAME_BITCOIN,
Annex, BlockHash, BlockHeader, BlockMerkleRoot, ControlBlock, InternalPk, InvalidLeafVer,
LeafVer, LockTime, Outpoint, Parity, RedeemScript, Sats, ScriptBytes, ScriptPubkey, SeqNo,
SigScript, Sighash, TapBranchHash, TapLeafHash, TapMerklePath, TapScript, Tx, TxIn, TxOut,
TxVer, Txid, Vout, Witness, WitnessScript, LIB_NAME_BITCOIN, TAPROOT_ANNEX_PREFIX,
};

/// Bitcoin consensus allows arrays which length is encoded as VarInt to grow up
/// to 64-bit values. However, at the same time no consensus rule allows any
/// block data structure to exceed 2^32 bytes (4GB), and any change to that rule
/// will be a hardfork. So for practical reasons we are safe to restrict the
/// maximum size here with just 32 bits.
pub type VarIntArray<T> = Confined<Vec<T>, 0, U32>;
pub type VarIntArray<T, const MIN_LEN: usize = 0> = Confined<Vec<T>, MIN_LEN, U32>;

pub type VarIntBytes = Confined<Vec<u8>, 0, U32>;
pub type VarIntBytes<const MIN_LEN: usize = 0> = Confined<Vec<u8>, MIN_LEN, U32>;

/// A variable-length unsigned integer.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
Expand Down Expand Up @@ -82,7 +82,7 @@ pub trait LenVarInt {
fn len_var_int(&self) -> VarInt;
}

impl<T> LenVarInt for VarIntArray<T> {
impl<T, const MIN_LEN: usize> LenVarInt for VarIntArray<T, MIN_LEN> {
fn len_var_int(&self) -> VarInt { VarInt::with(self.len()) }
}

Expand Down Expand Up @@ -190,6 +190,9 @@ pub enum ConsensusDataError {
#[display(inner)]
InvalidLeafVer(InvalidLeafVer),

/// invalid first annex byte `{0:#02x}`, which must be `0x50`.
WrongAnnexFirstByte(u8),

#[from]
#[display(inner)]
Confined(confinement::Error),
Expand Down Expand Up @@ -459,7 +462,7 @@ impl ConsensusDecode for LockTime {

impl ConsensusEncode for ScriptBytes {
fn consensus_encode(&self, writer: &mut impl Write) -> Result<usize, IoError> {
self.as_var_int_array().consensus_encode(writer)
self.as_var_int_bytes().consensus_encode(writer)
}
}

Expand Down Expand Up @@ -529,6 +532,22 @@ impl ConsensusDecode for SigScript {
}
}

impl ConsensusEncode for Annex {
fn consensus_encode(&self, writer: &mut impl Write) -> Result<usize, IoError> {
self.as_var_int_bytes().consensus_encode(writer)
}
}

impl ConsensusDecode for Annex {
fn consensus_decode(reader: &mut impl Read) -> Result<Self, ConsensusDecodeError> {
let bytes = VarIntBytes::<1>::consensus_decode(reader)?;
if bytes[0] != TAPROOT_ANNEX_PREFIX {
return Err(ConsensusDataError::WrongAnnexFirstByte(bytes[0]).into());
}
Ok(Self::from(bytes))
}
}

impl ConsensusEncode for Witness {
fn consensus_encode(&self, writer: &mut impl Write) -> Result<usize, IoError> {
self.as_var_int_array().consensus_encode(writer)
Expand Down Expand Up @@ -628,6 +647,18 @@ impl ConsensusDecode for Sats {
}
}

impl ConsensusEncode for Sighash {
fn consensus_encode(&self, writer: &mut impl Write) -> Result<usize, IoError> {
self.to_byte_array().consensus_encode(writer)
}
}

impl ConsensusEncode for TapLeafHash {
fn consensus_encode(&self, writer: &mut impl Write) -> Result<usize, IoError> {
self.to_byte_array().consensus_encode(writer)
}
}

impl ConsensusEncode for VarInt {
fn consensus_encode(&self, writer: &mut impl Write) -> Result<usize, IoError> {
match self.0 {
Expand Down Expand Up @@ -699,7 +730,7 @@ impl ConsensusDecode for ByteStr {
}
}

impl<T: ConsensusEncode> ConsensusEncode for VarIntArray<T> {
impl<T: ConsensusEncode, const MIN_LEN: usize> ConsensusEncode for VarIntArray<T, MIN_LEN> {
fn consensus_encode(&self, writer: &mut impl Write) -> Result<usize, IoError> {
let mut counter = self.len_var_int().consensus_encode(writer)?;
for item in self {
Expand All @@ -709,14 +740,14 @@ impl<T: ConsensusEncode> ConsensusEncode for VarIntArray<T> {
}
}

impl<T: ConsensusDecode> ConsensusDecode for VarIntArray<T> {
impl<T: ConsensusDecode, const MIN_LEN: usize> ConsensusDecode for VarIntArray<T, MIN_LEN> {
fn consensus_decode(reader: &mut impl Read) -> Result<Self, ConsensusDecodeError> {
let len = VarInt::consensus_decode(reader)?;
let mut arr = Vec::new();
for _ in 0..len.0 {
arr.push(T::consensus_decode(reader)?);
}
VarIntArray::try_from(arr).map_err(ConsensusDecodeError::from)
VarIntArray::<T, MIN_LEN>::try_from(arr).map_err(ConsensusDecodeError::from)
}
}

Expand Down Expand Up @@ -795,6 +826,20 @@ impl ConsensusDecode for u64 {
}
}

impl ConsensusEncode for Bytes32 {
fn consensus_encode(&self, writer: &mut impl Write) -> Result<usize, IoError> {
writer.write_all(&self.to_byte_array())?;
Ok(8)
}
}

impl ConsensusEncode for [u8; 32] {
fn consensus_encode(&self, writer: &mut impl Write) -> Result<usize, IoError> {
writer.write_all(self)?;
Ok(8)
}
}

impl ConsensusDecode for [u8; 32] {
fn consensus_decode(reader: &mut impl Read) -> Result<Self, ConsensusDecodeError> {
let mut buf = [0u8; 32];
Expand Down
12 changes: 7 additions & 5 deletions consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ mod weights;
#[cfg(feature = "stl")]
pub mod stl;
mod coding;
mod sigcache;

pub use block::{BlockHash, BlockHeader, BlockMerkleRoot};
pub use coding::{
Expand All @@ -76,12 +77,13 @@ pub use opcodes::OpCode;
pub use pubkeys::{CompressedPk, InvalidPubkey, LegacyPk, PubkeyParseError, UncompressedPk};
pub use script::{RedeemScript, ScriptBytes, ScriptPubkey, SigScript};
pub use segwit::{SegwitError, Witness, WitnessProgram, WitnessScript, WitnessVer, Wtxid};
pub use sigtypes::{Bip340Sig, LegacySig, SigError, SighashFlag, SighashType};
pub use sigcache::{PrevoutMismatch, SighashCache, SighashError};
pub use sigtypes::{Bip340Sig, LegacySig, ScriptCode, SigError, Sighash, SighashFlag, SighashType};
pub use taproot::{
ControlBlock, FutureLeafVer, InternalPk, IntoTapHash, InvalidLeafVer, InvalidParityValue,
LeafScript, LeafVer, OutputPk, Parity, TapBranchHash, TapCode, TapLeafHash, TapMerklePath,
TapNodeHash, TapScript, XOnlyPk, MIDSTATE_TAPSIGHASH, TAPROOT_ANNEX_PREFIX, TAPROOT_LEAF_MASK,
TAPROOT_LEAF_TAPSCRIPT,
Annex, AnnexError, ControlBlock, FutureLeafVer, InternalKeypair, InternalPk, IntoTapHash,
InvalidLeafVer, InvalidParityValue, LeafScript, LeafVer, OutputPk, Parity, TapBranchHash,
TapCode, TapLeafHash, TapMerklePath, TapNodeHash, TapScript, TapSighash, XOnlyPk,
MIDSTATE_TAPSIGHASH, TAPROOT_ANNEX_PREFIX, TAPROOT_LEAF_MASK, TAPROOT_LEAF_TAPSCRIPT,
};
pub use timelocks::{
InvalidTimelock, LockHeight, LockTime, LockTimestamp, SeqNo, TimelockParseError,
Expand Down
27 changes: 25 additions & 2 deletions consensus/src/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use amplify::confinement;
use amplify::confinement::Confined;

use crate::opcodes::*;
use crate::{ScriptHash, VarInt, VarIntArray, VarIntBytes, LIB_NAME_BITCOIN};
use crate::{ScriptHash, VarInt, VarIntBytes, WitnessVer, LIB_NAME_BITCOIN};

#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
#[wrapper(Deref, AsSlice, Hex)]
Expand Down Expand Up @@ -193,6 +193,29 @@ impl RedeemScript {
Self(ScriptBytes::from_unsafe(script_bytes))
}

pub fn p2sh_wpkh(hash: impl Into<[u8; 20]>) -> Self {
Self::with_witness_program_unchecked(WitnessVer::V0, &hash.into())
}

pub fn p2sh_wsh(hash: impl Into<[u8; 32]>) -> Self {
Self::with_witness_program_unchecked(WitnessVer::V0, &hash.into())
}

fn with_witness_program_unchecked(ver: WitnessVer, prog: &[u8]) -> Self {
let mut script = Self::with_capacity(ScriptBytes::len_for_slice(prog.len()) + 2);
script.push_opcode(ver.op_code());
script.push_slice(prog);
script
}

pub fn is_p2sh_wpkh(&self) -> bool {
self.len() == 22 && self[0] == WitnessVer::V0.op_code() as u8 && self[1] == OP_PUSHBYTES_20
}

pub fn is_p2sh_wsh(&self) -> bool {
self.len() == 34 && self[0] == WitnessVer::V0.op_code() as u8 && self[1] == OP_PUSHBYTES_32
}

/// Adds a single opcode to the script.
#[inline]
pub fn push_opcode(&mut self, op_code: OpCode) { self.0.push(op_code as u8); }
Expand Down Expand Up @@ -285,7 +308,7 @@ impl ScriptBytes {

pub fn into_vec(self) -> Vec<u8> { self.0.into_inner() }

pub(crate) fn as_var_int_array(&self) -> &VarIntArray<u8> { &self.0 }
pub(crate) fn as_var_int_bytes(&self) -> &VarIntBytes { &self.0 }
}

#[cfg(feature = "serde")]
Expand Down
Loading

0 comments on commit 1989b57

Please sign in to comment.