use std::io;
use bitcoin::Amount;
use bitcoin::secp256k1::PublicKey;
use elements::AssetId;
use elements::opcodes::all::*;
use elements::script::Builder;
pub fn divide_witness_pushes(
witness: &mut Vec<Vec<u8>>,
n: usize,
reverse: bool,
total_push: &[u8],
) {
const STD_MAX_SIZE: usize = 80;
const CNS_MAX_SIZE: usize = 520;
if total_push.len() > n * CNS_MAX_SIZE {
panic!("tried to push more bytes than space available: {} bytes ({} available)",
total_push.len(), n * CNS_MAX_SIZE,
);
}
let elem_size = if total_push.len() <= n * STD_MAX_SIZE {
STD_MAX_SIZE
} else {
CNS_MAX_SIZE
};
let mut pushes = Vec::with_capacity(n);
for chunk in total_push.chunks(elem_size) {
pushes.push(chunk.to_vec());
}
assert!(pushes.len() <= n);
pushes.resize(n, Vec::new());
if reverse {
witness.extend(pushes.into_iter().rev());
} else {
witness.extend(pushes.into_iter());
}
}
pub fn burn_output(amount: Amount, asset: AssetId) -> elements::TxOut {
elements::TxOut {
value: elements::confidential::Value::Explicit(amount.to_sat()),
asset: elements::confidential::Asset::Explicit(asset),
nonce: elements::confidential::Nonce::Null,
script_pubkey: Builder::new().push_opcode(OP_RETURN).into_script(),
witness: elements::TxOutWitness::default(),
}
}
pub trait BuilderExt: Into<Builder> + From<Builder> {
fn repeat(self, n: usize, f: impl Fn(Builder) -> Builder) -> Self {
let mut builder = self.into();
for _ in 0..n {
builder = f(builder);
}
builder.into()
}
fn check_stack_item_size(self, size: i64) -> Self {
self.into()
.push_opcode(OP_SIZE)
.push_int(size)
.push_opcode(OP_EQUALVERIFY)
.into()
}
fn spend_with_locktime(self, pubkey: &PublicKey, lock_time: elements::LockTime) -> Self {
self.into()
.push_int(lock_time.to_consensus_u32() as i64)
.push_opcode(OP_CLTV)
.push_opcode(OP_DROP)
.push_slice(&pubkey.serialize())
.push_opcode(OP_CHECKSIGVERIFY)
.into()
}
}
pub fn scriptint_size(int: i64) -> usize {
let script = Builder::new()
.push_int(int)
.into_script();
let len = script.encoded_len();
len - 1
}
impl BuilderExt for Builder {}
pub trait BitcoinEncodableExt: bitcoin::consensus::encode::Encodable {
fn encoded_len(&self) -> usize {
let mut counter = ByteCountSink::default();
self.consensus_encode(&mut counter).unwrap();
counter.count
}
}
impl<T: bitcoin::consensus::encode::Encodable + ?Sized> BitcoinEncodableExt for T {}
pub trait ElementsEncodableExt: elements::encode::Encodable {
fn encoded_len(&self) -> usize {
let mut counter = ByteCountSink::default();
self.consensus_encode(&mut counter).unwrap();
counter.count
}
}
impl<T: elements::encode::Encodable + ?Sized> ElementsEncodableExt for T {}
#[derive(Default)]
struct ByteCountSink {
count: usize,
}
impl io::Write for ByteCountSink {
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
let len = buf.len();
self.count += len;
Ok(len)
}
fn flush(&mut self) -> Result<(), io::Error> {
Ok(())
}
}
pub trait ReadExt: io::Read {
fn take_bytes(&mut self, n: usize) -> Result<Vec<u8>, io::Error> {
let mut buf = vec![0; n];
self.read_exact(&mut buf)?;
Ok(buf)
}
}
impl<T: AsRef<[u8]>> ReadExt for io::Cursor<T> {}
#[cfg(test)]
mod test {
use super::*;
use hex_conservative::FromHex;
#[test]
fn test_divide_witness_pushes() {
let total = vec![1, 2, 3];
let mut witness = vec![];
divide_witness_pushes(&mut witness, 3, false, &total[..]);
assert_eq!(witness, vec![vec![1,2,3], vec![], vec![]]);
let bytes_80 = Vec::<u8>::from_hex("05dcafc73348d3e030357c8c29666e47399930238e7e5d459236da467305a39111cb6f29c93c3c571c9258e5c57dd5e860dd52f6b56dfeb8c9f4dc59f62bf455178c556b2c1d24913f46831ca23028c5").unwrap();
let mut total = bytes_80.clone();
total.extend_from_slice(&[1, 2]);
let mut witness = vec![];
divide_witness_pushes(&mut witness, 3, false, &total[..]);
assert_eq!(witness, vec![bytes_80.clone(), vec![1, 2], vec![]]);
}
}