use std::fmt;
use bitcoin;
use elements::encode::{serialize, Encodable};
use elements::hashes::{sha256d, Hash};
use elements::{self, script, secp256k1_zkp, Script};
use super::super::checksum::{desc_checksum, verify_checksum};
use super::super::ELMTS_STR;
use super::{CovError, CovOperations};
use crate::expression::{self, FromTree};
use crate::extensions::ParseableExt;
use crate::miniscript::lex::{lex, Token as Tk, TokenIter};
use crate::miniscript::limits::{
MAX_OPS_PER_SCRIPT, MAX_SCRIPT_SIZE, MAX_STANDARD_P2WSH_SCRIPT_SIZE,
};
use crate::miniscript::{decode, types};
use crate::util::varint_len;
use crate::{
Error, ExtTranslator, Extension, ForEachKey, Miniscript, MiniscriptKey, Satisfier,
ScriptContext, Segwitv0, ToPublicKey, TranslateExt, TranslatePk, Translator,
};
fn hash256_arr<T: Encodable>(sl: &[T]) -> sha256d::Hash {
let mut enc = sha256d::Hash::engine();
for elem in sl {
elem.consensus_encode(&mut enc).unwrap();
}
sha256d::Hash::from_engine(enc)
}
pub(crate) const COV_SCRIPT_SIZE: usize = 120;
pub(crate) const COV_SCRIPT_OPCODE_COST: usize = 74;
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct LegacyCSFSCov<Pk: MiniscriptKey, Ext: Extension> {
pub(crate) pk: Pk,
pub(crate) ms: Miniscript<Pk, Segwitv0, Ext>,
}
impl<Pk: MiniscriptKey, Ext: Extension> LegacyCSFSCov<Pk, Ext> {
pub fn pk(&self) -> &Pk {
&self.pk
}
pub fn to_ms(&self) -> &Miniscript<Pk, Segwitv0, Ext> {
&self.ms
}
pub fn into_ms(self) -> Miniscript<Pk, Segwitv0, Ext> {
self.ms
}
pub fn new(pk: Pk, ms: Miniscript<Pk, Segwitv0, Ext>) -> Result<Self, Error> {
let ms_op_count = ms.ext.ops.op_count();
let cov_script_ops = COV_SCRIPT_OPCODE_COST;
let total_ops = ms_op_count.ok_or(Error::ImpossibleSatisfaction)? + cov_script_ops
- if ms.ext.has_free_verify { 1 } else { 0 };
if total_ops > MAX_OPS_PER_SCRIPT {
return Err(Error::ImpossibleSatisfaction);
}
let ss = COV_SCRIPT_SIZE - if ms.ext.has_free_verify { 1 } else { 0 };
if ms.script_size() + ss > MAX_SCRIPT_SIZE {
Err(Error::ScriptSizeTooLarge)
} else {
Ok(Self { pk, ms })
}
}
pub fn encode(&self) -> Script
where
Pk: ToPublicKey,
Ext: ParseableExt,
{
let builder = self.ms.node.encode(script::Builder::new());
builder.verify_cov(&self.pk.to_public_key()).into_script()
}
pub fn satisfy<S: Satisfier<Pk>>(&self, s: S, allow_mall: bool) -> Result<Vec<Vec<u8>>, Error>
where
Pk: ToPublicKey,
Ext: ParseableExt,
{
let mut wit = {
use crate::descriptor::CovError::MissingSighashItem;
let n_version = s.lookup_nversion().ok_or(MissingSighashItem(1))?;
let hash_prevouts = s.lookup_hashprevouts().ok_or(MissingSighashItem(1))?;
let hash_sequence = s.lookup_hashsequence().ok_or(MissingSighashItem(3))?;
let hash_issuances = s.lookup_hashissuances().ok_or(MissingSighashItem(3))?;
let outpoint = s.lookup_outpoint().ok_or(MissingSighashItem(4))?;
let script_code = s.lookup_scriptcode().ok_or(MissingSighashItem(5))?;
let value = s.lookup_value().ok_or(MissingSighashItem(6))?;
let n_sequence = s.lookup_nsequence().ok_or(MissingSighashItem(7))?;
let outputs = s.lookup_outputs().ok_or(MissingSighashItem(8))?;
let hash_outputs = hash256_arr(outputs);
let n_locktime = s.lookup_nlocktime().ok_or(MissingSighashItem(9))?;
let sighash_ty = s.lookup_sighashu32().ok_or(MissingSighashItem(10))?;
let (sig, hash_ty) = s
.lookup_ecdsa_sig(&self.pk)
.ok_or(CovError::MissingCovSignature)?;
if sighash_ty != hash_ty.as_u32() {
return Err(CovError::CovenantSighashTypeMismatch)?;
}
vec![
Vec::from(sig.serialize_der().as_ref()), serialize(&sighash_ty), serialize(&n_locktime), serialize(&hash_outputs), serialize(&n_sequence), serialize(&value), serialize(script_code), serialize(&outpoint), serialize(&hash_issuances), serialize(&hash_sequence), serialize(&hash_prevouts), serialize(&n_version), ]
};
let ms_wit = if !allow_mall {
self.ms.satisfy(s)?
} else {
self.ms.satisfy_malleable(s)?
};
wit.extend(ms_wit);
Ok(wit)
}
pub fn cov_script_code(&self) -> Script
where
Pk: ToPublicKey,
{
script::Builder::new().post_codesep_script().into_script()
}
}
impl<Ext: ParseableExt> LegacyCSFSCov<bitcoin::PublicKey, Ext> {
#[allow(unreachable_patterns)]
fn check_cov_script(tokens: &mut TokenIter<'_>) -> Result<bitcoin::PublicKey, Error> {
match_token!(tokens,
Tk::CheckSigFromStack, Tk::Verify, Tk::CheckSig, Tk::CodeSep, Tk::Swap,
Tk::FromAltStack, Tk::Dup, Tk::Bytes33(pk), Tk::Sha256,
Tk::Cat, Tk::Verify, Tk::Equal, Tk::Num(4), Tk::Size, Tk::Swap, Tk::Cat, Tk::Verify, Tk::Equal, Tk::Num(4), Tk::Size, Tk::Swap, Tk::Cat, Tk::Verify, Tk::Equal, Tk::Num(32), Tk::Size, Tk::Swap, Tk::Cat, Tk::Verify, Tk::Equal, Tk::Num(4), Tk::Size, Tk::Swap, Tk::Cat, Tk::EndIf,
Tk::Verify, Tk::Equal, Tk::Num(33), Tk::Size, Tk::Else,Tk::Verify, Tk::Equal, Tk::Num(9), Tk::Size, Tk::If, Tk::Equal, Tk::Num(1), Tk::Left, Tk::Num(1), Tk::Dup, Tk::Swap, Tk::Cat, Tk::Verify, Tk::Equal, Tk::Num(3), Tk::Size, Tk::Swap, Tk::Cat, Tk::Verify, Tk::Equal, Tk::Num(36), Tk::Size, Tk::Swap, Tk::Cat, Tk::Verify, Tk::Equal, Tk::Num(32), Tk::Size, Tk::Swap, Tk::Cat, Tk::Verify, Tk::Equal, Tk::Num(32), Tk::Size, Tk::Swap, Tk::Cat, Tk::Verify, Tk::Equal, Tk::Num(32), Tk::Size, Tk::Swap, Tk::Verify, Tk::Equal, Tk::Num(4), Tk::Size, Tk::ToAltStack, Tk::Cat, Tk::Left, Tk::Num(1),
Tk::Pick, Tk::Num(11), Tk::Pick, Tk::Num(11), Tk::Verify => {
Ok(bitcoin::PublicKey::from_slice(pk)?)
},
_ => Err(Error::CovError(CovError::BadCovDescriptor)),
)
}
pub fn parse_insane(script: &script::Script) -> Result<Self, Error> {
let (pk, ms) = Self::parse_cov_components(script)?;
Self::new(pk, ms)
}
pub(crate) fn parse_cov_components(
script: &script::Script,
) -> Result<
(
bitcoin::PublicKey,
Miniscript<bitcoin::PublicKey, Segwitv0, Ext>,
),
Error,
>
where
Ext: ParseableExt,
{
let tokens = lex(script)?;
let mut iter = TokenIter::new(tokens);
let pk = LegacyCSFSCov::<bitcoin::PublicKey, Ext>::check_cov_script(&mut iter)?;
let ms = decode::parse(&mut iter)?;
Segwitv0::check_global_validity(&ms)?;
if ms.ty.corr.base != types::Base::B {
return Err(Error::NonTopLevel(format!("{:?}", ms)));
};
if let Some(leading) = iter.next() {
Err(Error::Trailing(leading.to_string()))
} else {
Ok((pk, ms))
}
}
pub fn parse(script: &script::Script) -> Result<Self, Error> {
let cov = Self::parse_insane(script)?;
cov.ms.sanity_check()?;
Ok(cov)
}
}
impl_from_tree!(
LegacyCSFSCov<Pk, Ext>,
=> Ext; Extension,
fn from_tree(top: &expression::Tree<'_>) -> Result<Self, Error> {
if top.name == "elcovwsh" && top.args.len() == 2 {
let pk = expression::terminal(&top.args[0], |pk| Pk::from_str(pk))?;
let top = &top.args[1];
let sub = Miniscript::from_tree(top)?;
Segwitv0::top_level_checks(&sub)?;
Ok(LegacyCSFSCov { pk, ms: sub })
} else {
Err(Error::Unexpected(format!(
"{}({} args) while parsing elcovwsh descriptor",
top.name,
top.args.len(),
)))
}
}
);
impl<Pk, Ext> fmt::Debug for LegacyCSFSCov<Pk, Ext>
where
Pk: MiniscriptKey,
Ext: Extension,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}covwsh({},{})", ELMTS_STR, self.pk, self.ms)
}
}
impl<Pk, Ext> fmt::Display for LegacyCSFSCov<Pk, Ext>
where
Pk: MiniscriptKey,
Ext: Extension,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let desc = format!("{}covwsh({},{})", ELMTS_STR, self.pk, self.ms);
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
write!(f, "{}#{}", &desc, &checksum)
}
}
impl_from_str!(
LegacyCSFSCov<Pk, Ext>,
=> Ext; Extension,
type Err = Error;,
fn from_str(s: &str) -> Result<Self, Self::Err> {
let desc_str = verify_checksum(s)?;
let top = expression::Tree::from_str(desc_str)?;
LegacyCSFSCov::<Pk, Ext>::from_tree(&top)
}
);
impl<Pk, Ext> LegacyCSFSCov<Pk, Ext>
where
Pk: MiniscriptKey,
Ext: Extension,
{
pub fn sanity_check(&self) -> Result<(), Error> {
self.ms.sanity_check()?;
let ss = COV_SCRIPT_SIZE - if self.ms.ext.has_free_verify { 1 } else { 0 };
if self.ms.script_size() + ss > MAX_STANDARD_P2WSH_SCRIPT_SIZE {
Err(Error::ScriptSizeTooLarge)
} else {
Ok(())
}
}
pub fn address(
&self,
blinder: Option<secp256k1_zkp::PublicKey>,
params: &'static elements::AddressParams,
) -> elements::Address
where
Pk: ToPublicKey,
Ext: ParseableExt,
{
elements::Address::p2wsh(&self.encode(), blinder, params)
}
pub fn script_pubkey(&self) -> Script
where
Pk: ToPublicKey,
Ext: ParseableExt,
{
self.encode().to_v0_p2wsh()
}
pub fn unsigned_script_sig(&self) -> Script
where
Pk: ToPublicKey,
{
Script::new()
}
pub fn inner_script(&self) -> Script
where
Pk: ToPublicKey,
Ext: ParseableExt,
{
self.encode()
}
pub fn get_satisfaction<S>(&self, satisfier: S) -> Result<(Vec<Vec<u8>>, Script), Error>
where
Pk: ToPublicKey,
S: Satisfier<Pk>,
Ext: ParseableExt,
{
let mut witness = self.satisfy(satisfier, false)?;
witness.push(self.encode().into_bytes());
let script_sig = Script::new();
Ok((witness, script_sig))
}
pub fn max_satisfaction_weight(&self) -> Result<usize, Error> {
let script_size =
self.ms.script_size() + 58 - if self.ms.ext.has_free_verify { 1 } else { 0 };
let max_sat_elems = self.ms.max_satisfaction_witness_elements()? + 12;
let max_sat_size = self.ms.max_satisfaction_size()? + 275;
Ok(4 + varint_len(script_size) +
script_size +
varint_len(max_sat_elems) +
max_sat_size)
}
pub fn ecdsa_sighash_script_code(&self) -> Script
where
Pk: ToPublicKey,
Ext: ParseableExt,
{
self.inner_script()
}
pub fn get_satisfaction_mall<S>(&self, satisfier: S) -> Result<(Vec<Vec<u8>>, Script), Error>
where
Pk: ToPublicKey,
S: Satisfier<Pk>,
Ext: ParseableExt,
{
let mut witness = self.satisfy(satisfier, true)?;
witness.push(self.encode().into_bytes());
let script_sig = Script::new();
Ok((witness, script_sig))
}
}
impl<Pk: MiniscriptKey, Ext: Extension> ForEachKey<Pk> for LegacyCSFSCov<Pk, Ext> {
fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool
where
Pk: 'a,
{
pred(&self.pk) && self.ms.for_any_key(pred)
}
}
impl<P, Q, Ext> TranslatePk<P, Q> for LegacyCSFSCov<P, Ext>
where
P: MiniscriptKey,
Q: MiniscriptKey,
Ext: Extension,
{
type Output = LegacyCSFSCov<Q, Ext>;
fn translate_pk<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
where
T: Translator<P, Q, E>,
{
Ok(LegacyCSFSCov {
pk: t.pk(&self.pk)?,
ms: self.ms.translate_pk(t)?,
})
}
}
impl<Pk, Ext, ExtQ> TranslateExt<Ext, ExtQ> for LegacyCSFSCov<Pk, Ext>
where
Pk: MiniscriptKey,
Ext: Extension,
ExtQ: Extension,
Ext: TranslateExt<Ext, ExtQ, Output = ExtQ>,
{
type Output = LegacyCSFSCov<Pk, ExtQ>;
fn translate_ext<T, E>(&self, translator: &mut T) -> Result<Self::Output, E>
where
T: ExtTranslator<Ext, ExtQ, E>,
{
Ok(LegacyCSFSCov {
pk: self.pk.clone(),
ms: self.ms.translate_ext(translator)?,
})
}
}