use std::io::{Read, Write};
use coins_core::{
hashes::{Digest, DigestOutput, Hash256, Hash256Digest, MarkedDigest, MarkedDigestOutput},
ser::{self, ByteFormat},
types::tx::Transaction,
};
use crate::{
hashes::{TXID, WTXID},
types::{
legacy::*,
script::{Script, Witness},
tx::*,
txin::BitcoinTxIn,
txout::TxOut,
},
};
pub trait WitnessTransaction: BitcoinTransaction {
type WTXID: MarkedDigestOutput;
type WitnessSighashArgs;
type Witness;
fn new<I, O, W>(
version: u32,
vin: I,
vout: O,
witnesses: W,
locktime: u32,
) -> Result<Self, Self::TxError>
where
I: Into<Vec<Self::TxIn>>,
O: Into<Vec<Self::TxOut>>,
W: Into<Vec<Self::Witness>>,
Self: Sized;
fn wtxid(&self) -> Self::WTXID;
fn write_legacy_sighash_preimage<W: Write>(
&self,
writer: &mut W,
args: &LegacySighashArgs,
) -> Result<(), Self::TxError>;
fn legacy_sighash(
&self,
args: &LegacySighashArgs,
) -> Result<DigestOutput<Self::HashWriter>, Self::TxError> {
let mut w = Self::HashWriter::default();
self.write_legacy_sighash_preimage(&mut w, args)?;
Ok(w.finalize())
}
fn write_witness_sighash_preimage<W: Write>(
&self,
writer: &mut W,
args: &Self::WitnessSighashArgs,
) -> Result<(), Self::TxError>;
fn witness_sighash(
&self,
args: &Self::WitnessSighashArgs,
) -> Result<DigestOutput<Self::HashWriter>, Self::TxError> {
let mut w = Self::HashWriter::default();
self.write_witness_sighash_preimage(&mut w, args)?;
Ok(w.finalize())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WitnessSighashArgs {
pub index: usize,
pub sighash_flag: Sighash,
pub prevout_script: Script,
pub prevout_value: u64,
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq, Default)]
pub struct WitnessTx {
pub(crate) legacy_tx: LegacyTx,
pub(crate) witnesses: Vec<Witness>,
}
impl WitnessTx {
fn hash_prevouts(&self, sighash_flag: Sighash) -> TxResult<Hash256Digest> {
if sighash_flag as u8 & 0x80 == 0x80 {
Ok(Hash256Digest::default())
} else {
let mut w = Hash256::default();
for input in self.legacy_tx.vin.iter() {
input.outpoint.write_to(&mut w)?;
}
Ok(w.finalize_marked())
}
}
fn hash_sequence(&self, sighash_flag: Sighash) -> TxResult<Hash256Digest> {
if sighash_flag == Sighash::Single || sighash_flag as u8 & 0x80 == 0x80 {
Ok(Hash256Digest::default())
} else {
let mut w = Hash256::default();
for input in self.legacy_tx.vin.iter() {
ser::write_u32_le(&mut w, input.sequence)?;
}
Ok(w.finalize_marked())
}
}
fn hash_outputs(&self, index: usize, sighash_flag: Sighash) -> TxResult<Hash256Digest> {
match sighash_flag {
Sighash::All | Sighash::AllAcp => {
let mut w = Hash256::default();
for output in self.legacy_tx.vout.iter() {
output.write_to(&mut w)?;
}
Ok(w.finalize_marked())
}
Sighash::Single | Sighash::SingleAcp => {
let mut w = Hash256::default();
self.legacy_tx.vout[index].write_to(&mut w)?;
Ok(w.finalize_marked())
}
_ => Ok(Hash256Digest::default()),
}
}
pub fn from_legacy(legacy_tx: LegacyTx) -> Self {
let witnesses = (0..legacy_tx.inputs().len())
.map(|_| Witness::default())
.collect();
Self {
legacy_tx,
witnesses,
}
}
}
impl Transaction for WitnessTx {
type TxError = TxError;
type TxIn = BitcoinTxIn;
type TxOut = TxOut;
type SighashArgs = WitnessSighashArgs;
type TXID = TXID;
type HashWriter = Hash256;
fn new<I, O>(version: u32, vin: I, vout: O, locktime: u32) -> Result<Self, Self::TxError>
where
I: Into<Vec<Self::TxIn>>,
O: Into<Vec<Self::TxOut>>,
Self: Sized,
{
let input_vector: Vec<BitcoinTxIn> = vin.into();
let witnesses = input_vector.iter().map(|_| Witness::default()).collect();
let legacy_tx = LegacyTx::new(version, input_vector, vout, locktime)?;
Ok(Self {
legacy_tx,
witnesses,
})
}
fn inputs(&self) -> &[Self::TxIn] {
&self.legacy_tx.vin
}
fn outputs(&self) -> &[Self::TxOut] {
&self.legacy_tx.vout
}
fn version(&self) -> u32 {
self.legacy_tx.version
}
fn locktime(&self) -> u32 {
self.legacy_tx.locktime
}
fn txid(&self) -> Self::TXID {
self.legacy_tx.txid()
}
fn write_sighash_preimage<W: Write>(
&self,
writer: &mut W,
args: &Self::SighashArgs,
) -> TxResult<()> {
self.write_witness_sighash_preimage(writer, args)
}
}
impl BitcoinTransaction for WitnessTx {
fn as_legacy(&self) -> &LegacyTx {
&self.legacy_tx
}
fn into_witness(self) -> WitnessTx {
self
}
fn into_legacy(self) -> LegacyTx {
self.legacy_tx
}
fn witnesses(&self) -> &[Witness] {
&self.witnesses
}
}
impl WitnessTransaction for WitnessTx {
type WTXID = WTXID;
type WitnessSighashArgs = WitnessSighashArgs;
type Witness = Witness;
fn new<I, O, W>(
version: u32,
vin: I,
vout: O,
witnesses: W,
locktime: u32,
) -> Result<Self, Self::TxError>
where
I: Into<Vec<Self::TxIn>>,
O: Into<Vec<Self::TxOut>>,
W: Into<Vec<Self::Witness>>,
Self: Sized,
{
let vins = vin.into();
let mut wits = witnesses.into();
if wits.len() != vins.len() {
wits.resize(vins.len(), Witness::default());
}
let legacy_tx = LegacyTx::new(version, vins, vout, locktime)?;
Ok(Self {
legacy_tx,
witnesses: wits,
})
}
fn wtxid(&self) -> Self::WTXID {
let mut w = Self::HashWriter::default();
self.write_to(&mut w).expect("No IOError from SHA2");
w.finalize_marked()
}
fn write_legacy_sighash_preimage<W: Write>(
&self,
writer: &mut W,
args: &LegacySighashArgs,
) -> Result<(), Self::TxError> {
self.legacy_tx.write_sighash_preimage(writer, args)
}
fn write_witness_sighash_preimage<W>(
&self,
writer: &mut W,
args: &WitnessSighashArgs,
) -> TxResult<()>
where
W: Write,
{
if args.sighash_flag == Sighash::None || args.sighash_flag == Sighash::NoneAcp {
return Err(TxError::NoneUnsupported);
}
if (args.sighash_flag == Sighash::Single || args.sighash_flag == Sighash::SingleAcp)
&& args.index >= self.outputs().len()
{
return Err(TxError::SighashSingleBug);
}
let input = &self.legacy_tx.vin[args.index];
ser::write_u32_le(writer, self.legacy_tx.version)?;
self.hash_prevouts(args.sighash_flag)?.write_to(writer)?;
self.hash_sequence(args.sighash_flag)?.write_to(writer)?;
input.outpoint.write_to(writer)?;
args.prevout_script.write_to(writer)?;
ser::write_u64_le(writer, args.prevout_value)?;
ser::write_u32_le(writer, input.sequence)?;
self.hash_outputs(args.index, args.sighash_flag)?
.write_to(writer)?;
ser::write_u32_le(writer, self.legacy_tx.locktime)?;
ser::write_u32_le(writer, args.sighash_flag as u32)?;
Ok(())
}
}
impl ByteFormat for WitnessTx {
type Error = TxError;
fn serialized_length(&self) -> usize {
let mut len = 4; len += 2; len += coins_core::ser::prefix_byte_len(self.legacy_tx.vin.len() as u64) as usize;
len += self
.legacy_tx
.vin
.iter()
.map(|i| i.serialized_length())
.sum::<usize>();
len += coins_core::ser::prefix_byte_len(self.legacy_tx.vout.len() as u64) as usize;
len += self
.legacy_tx
.vout
.iter()
.map(|o| o.serialized_length())
.sum::<usize>();
for witness in self.witnesses.iter() {
len += coins_core::ser::prefix_byte_len(self.witnesses.len() as u64) as usize;
len += witness.iter().map(|w| w.serialized_length()).sum::<usize>();
}
len += 4; len
}
fn read_from<R>(reader: &mut R) -> Result<Self, Self::Error>
where
R: Read,
Self: std::marker::Sized,
{
let version = ser::read_u32_le(reader)?;
let mut flag = [0u8; 2];
reader.read_exact(&mut flag)?;
if flag != [0u8, 1u8] {
return Err(TxError::BadWitnessFlag(flag));
};
let vin = ser::read_prefix_vec(reader)?;
let vout = ser::read_prefix_vec(reader)?;
let mut witnesses = vec![];
for _ in vin.iter() {
witnesses.push(ser::read_prefix_vec(reader)?);
}
let locktime = ser::read_u32_le(reader)?;
let legacy_tx = LegacyTx {
version,
vin,
vout,
locktime,
};
Ok(Self {
legacy_tx,
witnesses,
})
}
fn write_to<W>(&self, writer: &mut W) -> Result<usize, Self::Error>
where
W: Write,
{
let mut len = ser::write_u32_le(writer, self.version())?;
len += writer.write(&[0u8, 1u8])?;
len += ser::write_prefix_vec(writer, &self.legacy_tx.vin)?;
len += ser::write_prefix_vec(writer, &self.legacy_tx.vout)?;
for wit in self.witnesses.iter() {
len += ser::write_prefix_vec(writer, wit)?;
}
len += ser::write_u32_le(writer, self.locktime())?;
Ok(len)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::types::{BitcoinTxIn, TxOut, Witness, WitnessStackItem};
#[test]
fn it_should_ensure_correct_amount_of_witnesses_addition() {
let vin = vec![BitcoinTxIn::default(), BitcoinTxIn::default()];
let vout = vec![TxOut::default()];
let witnesses = vec![];
let expect = vin.len();
let tx = <WitnessTx as WitnessTransaction>::new(2, vin, vout, witnesses, 0).unwrap();
assert_eq!(tx.witnesses.len(), expect);
}
#[test]
fn it_should_ensure_correct_amount_of_witnesses_subtraction() {
let vin = vec![BitcoinTxIn::default()];
let vout = vec![TxOut::default()];
let expected_witness = vec![WitnessStackItem::new(vec![1, 2, 3, 4])];
let witnesses = vec![expected_witness.clone(), Witness::default()];
let expected_size = vin.len();
let tx = <WitnessTx as WitnessTransaction>::new(2, vin, vout, witnesses, 0).unwrap();
assert_eq!(tx.witnesses.len(), expected_size);
assert_eq!(expected_witness, tx.witnesses[0]);
}
}