use std::io::{Read, Write};
use coins_core::{
hashes::*,
ser::{self, ByteFormat},
types::tx::Transaction,
};
use crate::{
hashes::TXID,
types::{
script::{Script, ScriptSig, Witness},
tx::*,
txin::{BitcoinTxIn, Vin},
txout::{TxOut, Vout},
witness::*,
},
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LegacySighashArgs {
pub index: usize,
pub sighash_flag: Sighash,
pub prevout_script: Script,
}
impl From<&crate::types::witness::WitnessSighashArgs> for LegacySighashArgs {
fn from(s: &crate::types::witness::WitnessSighashArgs) -> Self {
Self {
index: s.index,
sighash_flag: s.sighash_flag,
prevout_script: s.prevout_script.clone(),
}
}
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq, Default)]
pub struct LegacyTx {
pub(crate) version: u32,
pub(crate) vin: Vin,
pub(crate) vout: Vout,
pub(crate) locktime: u32,
}
impl LegacyTx {
fn legacy_sighash_prep(&self, index: usize, prevout_script: &Script) -> Self {
let mut copy_tx = self.clone();
for i in 0..copy_tx.vin.len() {
copy_tx.vin[i].script_sig = if i == index {
ScriptSig::from(prevout_script.items())
} else {
ScriptSig::null()
};
}
copy_tx
}
fn legacy_sighash_single(copy_tx: &mut Self, index: usize) {
let mut tx_outs: Vec<TxOut> = (0..index).map(|_| TxOut::null()).collect();
tx_outs.push(copy_tx.vout[index].clone());
copy_tx.vout = tx_outs;
let mut vin = vec![];
for i in 0..copy_tx.vin.len() {
let mut txin = copy_tx.vin[i].clone();
if i != index {
txin.sequence = 0;
}
vin.push(txin);
}
copy_tx.vin = vin;
}
fn legacy_sighash_anyone_can_pay(copy_tx: &mut Self, index: usize) {
copy_tx.vin = vec![copy_tx.vin[index].clone()];
}
}
impl Transaction for LegacyTx {
type TxError = TxError;
type TxIn = BitcoinTxIn;
type TxOut = TxOut;
type SighashArgs = LegacySighashArgs;
type TXID = TXID;
type HashWriter = Hash256;
fn new<I, O>(version: u32, vin: I, vout: O, locktime: u32) -> Result<Self, Self::Error>
where
I: Into<Vec<Self::TxIn>>,
O: Into<Vec<Self::TxOut>>,
Self: Sized,
{
let vins = vin.into();
let vouts = vout.into();
if vins.is_empty() {
return Err(TxError::EmptyVin);
}
if vouts.is_empty() {
return Err(TxError::EmptyVout);
}
Ok(Self {
version,
vin: vins,
vout: vouts,
locktime,
})
}
fn inputs(&self) -> &[Self::TxIn] {
&self.vin
}
fn outputs(&self) -> &[Self::TxOut] {
&self.vout
}
fn version(&self) -> u32 {
self.version
}
fn locktime(&self) -> u32 {
self.locktime
}
fn write_sighash_preimage<W: Write>(
&self,
writer: &mut W,
args: &LegacySighashArgs,
) -> TxResult<()> {
if args.sighash_flag == Sighash::None || args.sighash_flag == Sighash::NoneAcp {
return Err(TxError::NoneUnsupported);
}
let mut copy_tx: Self = self.legacy_sighash_prep(args.index, &args.prevout_script);
if args.sighash_flag == Sighash::Single || args.sighash_flag == Sighash::SingleAcp {
if args.index >= self.outputs().len() {
return Err(TxError::SighashSingleBug);
}
Self::legacy_sighash_single(&mut copy_tx, args.index);
}
if args.sighash_flag as u8 & 0x80 == 0x80 {
Self::legacy_sighash_anyone_can_pay(&mut copy_tx, args.index);
}
copy_tx.write_to(writer)?;
coins_core::ser::write_u32_le(writer, args.sighash_flag as u32)?;
Ok(())
}
}
impl BitcoinTransaction for LegacyTx {
fn as_legacy(&self) -> &LegacyTx {
self
}
fn into_witness(self) -> WitnessTx {
WitnessTx::from_legacy(self)
}
fn into_legacy(self) -> LegacyTx {
self
}
fn witnesses(&self) -> &[Witness] {
&[]
}
}
impl ByteFormat for LegacyTx {
type Error = TxError;
fn serialized_length(&self) -> usize {
let mut len = 4; len += coins_core::ser::prefix_byte_len(self.vin.len() as u64) as usize;
len += self
.vin
.iter()
.map(|i| i.serialized_length())
.sum::<usize>();
len += coins_core::ser::prefix_byte_len(self.vout.len() as u64) as usize;
len += self
.vout
.iter()
.map(|o| o.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 = coins_core::ser::read_u32_le(reader)?;
let vin = ser::read_prefix_vec(reader)?;
let vout = ser::read_prefix_vec(reader)?;
let locktime = coins_core::ser::read_u32_le(reader)?;
Ok(Self {
version,
vin,
vout,
locktime,
})
}
fn write_to<W>(&self, writer: &mut W) -> Result<usize, Self::Error>
where
W: Write,
{
let mut len = coins_core::ser::write_u32_le(writer, self.version())?;
ser::write_prefix_vec(writer, &self.vin)?;
ser::write_prefix_vec(writer, &self.vout)?;
len += coins_core::ser::write_u32_le(writer, self.locktime())?;
Ok(len)
}
}