use alloc::vec::Vec;
use ripemd::Ripemd160;
use sha2::{Digest, Sha256};
use thiserror::Error;
use crate::{
num, op, opcode, pv,
script::{self, Evaluable},
Opcode::{self, PushValue},
};
pub const EMPTY_STACK_CHECK: [Opcode; 3] = [op::DEPTH, op::_0, op::EQUAL];
pub fn ignored_value(v: opcode::PushValue) -> [Opcode; 2] {
[PushValue(v), op::DROP]
}
pub fn branch(thn: &[Opcode], els: &[Opcode]) -> Vec<Opcode> {
[&[op::IF], thn, &[op::ELSE], els, &[op::ENDIF]].concat()
}
pub fn if_else(cond: &[Opcode], thn: &[Opcode], els: &[Opcode]) -> Vec<Opcode> {
let mut vec = cond.to_vec();
vec.extend(branch(thn, els));
vec
}
#[allow(missing_docs)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Error)]
pub enum Error {
#[error("CHECKMULTISIG only supports 20 keys, but you provided {0}")]
TooManyPubKeys(usize),
#[error("PubKeys shouldn’t be longer than 65 bytes")]
OverlongPubKey,
}
pub fn push_pubkey(pubkey: &[u8]) -> Result<Opcode, Error> {
pv::push_value(pubkey)
.map(PushValue)
.ok_or(Error::OverlongPubKey)
}
pub fn check_multisig(sig_count: u8, pks: &[&[u8]], verify: bool) -> Result<Vec<Opcode>, Error> {
Ok([
&[PushValue(push_num(sig_count.into()))],
&pks.iter()
.map(|pk| push_pubkey(pk))
.collect::<Result<Vec<_>, _>>()?[..],
&[
PushValue(push_num(
pks.len()
.try_into()
.map_err(|_| Error::TooManyPubKeys(pks.len()))?,
)),
if verify {
op::CHECKMULTISIGVERIFY
} else {
op::CHECKMULTISIG
},
],
]
.concat())
}
pub fn equals(expected: opcode::PushValue, verify: bool) -> [Opcode; 2] {
[
PushValue(expected),
if verify { op::EQUALVERIFY } else { op::EQUAL },
]
}
pub fn check_sig(pubkey: &[u8], verify: bool) -> Result<[Opcode; 2], Error> {
Ok([
push_pubkey(pubkey)?,
if verify {
op::CHECKSIGVERIFY
} else {
op::CHECKSIG
},
])
}
pub fn size_check(expected: u32) -> Vec<Opcode> {
[&[op::SIZE], &equals(push_num(expected.into()), true)[..]].concat()
}
pub fn check_lock_time_verify(lt: u32) -> [Opcode; 3] {
[
PushValue(push_num(lt.into())),
op::CHECKLOCKTIMEVERIFY,
op::DROP,
]
}
pub fn push_num(n: i64) -> opcode::PushValue {
pv::push_value(&num::serialize(n)).expect("all i64 can be encoded as `PushValue`")
}
pub fn push_script<T: Into<opcode::PossiblyBad> + opcode::Evaluable + Clone>(
script: &script::Component<T>,
) -> Option<opcode::PushValue> {
pv::push_value(&script.to_bytes())
}
pub fn push_160b_hash(hash: &[u8; 20]) -> opcode::PushValue {
pv::push_value(hash).expect("20 is a valid data size")
}
pub fn push_256b_hash(hash: &[u8; 32]) -> opcode::PushValue {
pv::push_value(hash).expect("32 is a valid data size")
}
pub fn pay_to_pubkey(pubkey: &[u8]) -> Result<[Opcode; 2], Error> {
check_sig(pubkey, false)
}
pub fn pay_to_pubkey_hash(pk: &[u8]) -> Vec<Opcode> {
[
&[op::DUP, op::HASH160],
&equals(
push_160b_hash(&Ripemd160::digest(Sha256::digest(pk)).into()),
true,
)[..],
&[op::CHECKSIG],
]
.concat()
}
pub fn pay_to_script_hash<T: Into<opcode::PossiblyBad> + opcode::Evaluable + Clone>(
redeem_script: &script::Component<T>,
) -> Vec<Opcode> {
[
&[op::HASH160],
&equals(
push_160b_hash(&Ripemd160::digest(Sha256::digest(redeem_script.to_bytes())).into()),
false,
)[..],
]
.concat()
}
pub fn check_multisig_empty(
n: u8,
x: &[&[u8]],
ignored: opcode::PushValue,
) -> Result<Vec<Opcode>, Error> {
Ok([
&check_multisig(n, x, true)?,
&ignored_value(ignored)[..],
&EMPTY_STACK_CHECK,
]
.concat())
}
pub fn combined_multisig(m: u8, x: &[&[u8]], n: u8, y: &[&[u8]]) -> Result<Vec<Opcode>, Error> {
Ok([
&check_multisig(m, x, true)?[..],
&check_multisig(n, y, false)?[..],
]
.concat())
}
pub fn p2pkh_ignored(ignored: opcode::PushValue, pk: &[u8]) -> Vec<Opcode> {
[&ignored_value(ignored)[..], &pay_to_pubkey_hash(pk)].concat()
}
pub fn p2pk_empty(recipient_pk: &[u8], ignored: opcode::PushValue) -> Result<Vec<Opcode>, Error> {
Ok([
&check_sig(recipient_pk, true)?[..],
&ignored_value(ignored)[..],
&EMPTY_STACK_CHECK,
]
.concat())
}
pub fn hash160_htlc(
lt: u32,
sender_pk: &[u8],
recipient_hash: &[u8; 20],
recipient_pk: &[u8],
) -> Result<Vec<Opcode>, Error> {
Ok(branch(
&[
&check_lock_time_verify(lt)[..],
&check_sig(sender_pk, false)?[..],
]
.concat(),
&[
&[op::HASH160],
&equals(push_160b_hash(recipient_hash), true)[..],
&check_sig(recipient_pk, false)?[..],
]
.concat(),
))
}
pub fn hash160_htlc_size_check(
lt: u32,
sender_pk: &[u8],
recipient_hash: &[u8; 20],
recipient_pk: &[u8],
) -> Result<Vec<Opcode>, Error> {
Ok(branch(
&[
&check_lock_time_verify(lt)[..],
&check_sig(sender_pk, false)?[..],
]
.concat(),
&[
&size_check(20)[..],
&[op::HASH160],
&equals(push_160b_hash(recipient_hash), true)[..],
&check_sig(recipient_pk, false)?[..],
]
.concat(),
))
}
pub fn hash160_htlc_p2pkh(
lt: u32,
sender_pk: &[u8],
recipient_hash: &[u8; 20],
recipient_pk: &[u8],
) -> Vec<Opcode> {
branch(
&[
&[op::HASH160],
&equals(push_160b_hash(recipient_hash), true)[..],
&pay_to_pubkey_hash(recipient_pk)[..],
]
.concat(),
&[
&check_lock_time_verify(lt)[..],
&pay_to_pubkey_hash(sender_pk)[..],
]
.concat(),
)
}
pub fn sha256_htlc(
lt: u32,
sender_pk: &[u8],
recipient_sha: &[u8; 32],
recipient_pk: &[u8],
) -> Result<Vec<Opcode>, Error> {
Ok(branch(
&[
&check_lock_time_verify(lt)[..],
&check_sig(sender_pk, false)?[..],
]
.concat(),
&[
&[op::SHA256],
&equals(push_256b_hash(recipient_sha), true)[..],
&check_sig(recipient_pk, false)?[..],
]
.concat(),
))
}
pub fn sha256_htlc_p2pkh(
lt: u32,
sender_pk: &[u8],
recipient_sha: &[u8; 32],
recipient_pk: &[u8],
) -> Vec<Opcode> {
branch(
&[
&[op::SHA256],
&equals(push_256b_hash(recipient_sha), true)[..],
&pay_to_pubkey_hash(recipient_pk)[..],
]
.concat(),
&[
&check_lock_time_verify(lt)[..],
&pay_to_pubkey_hash(sender_pk)[..],
]
.concat(),
)
}
pub fn sha256_htlc_size_check(
lt: u32,
sender_pk: &[u8],
recipient_sha: &[u8; 32],
recipient_pk: &[u8],
) -> Vec<Opcode> {
branch(
&[
&size_check(20)[..],
&[op::SHA256],
&equals(push_256b_hash(recipient_sha), true)[..],
&pay_to_pubkey_hash(recipient_pk)[..],
]
.concat(),
&[
&check_lock_time_verify(lt)[..],
&pay_to_pubkey_hash(sender_pk)[..],
]
.concat(),
)
}
pub fn sha256_htlc_with_unconditional(
sender_pk: &[u8],
recipient_sha: &[u8; 32],
recipient_pk: &[u8],
) -> Vec<Opcode> {
branch(
&[
&[op::SHA256],
&equals(push_256b_hash(recipient_sha), true)[..],
&pay_to_pubkey_hash(recipient_pk)[..],
]
.concat(),
&[
&ignored_value(pv::_1)[..],
&[op::HASH160],
&equals(
push_160b_hash(&Ripemd160::digest(Sha256::digest(sender_pk)).into()),
true,
)[..],
&[op::CHECKSIG],
]
.concat(),
)
}
pub fn dual_hash160_htlc_size_check(
lt: u32,
sender_hash: &[u8; 20],
sender_pk: &[u8],
recipient_hash: &[u8; 20],
recipient_pk: &[u8],
) -> Result<Vec<Opcode>, Error> {
let verify = |hash, pk| {
Ok([
&size_check(20)[..],
&[op::HASH160],
&equals(push_160b_hash(hash), true)[..],
&check_sig(pk, false)?[..],
]
.concat())
};
Ok(branch(
&[
&check_lock_time_verify(lt)[..],
&verify(sender_hash, sender_pk)?[..],
]
.concat(),
&verify(recipient_hash, recipient_pk)?,
))
}