use std::collections::BTreeMap;
use bitcoin::{Transaction, TxIn, TxOut};
use wallet::psbt::{self, Psbt, PsbtVersion};
use crate::channel::Funding;
pub trait TxRole: Clone + From<u16> + Into<u16> {}
pub trait TxIndex: Clone + From<u64> + Into<u64> {}
impl TxRole for u16 {}
impl TxIndex for u64 {}
#[derive(Getters, Clone, Eq, PartialEq)]
pub struct TxGraph<'channel> {
funding: &'channel Funding,
pub cmt_version: i32,
pub cmt_locktime: u32,
pub cmt_sequence: u32,
pub cmt_outs: Vec<psbt::Output>,
graph: BTreeMap<u16, BTreeMap<u64, Psbt>>,
}
impl<'channel> TxGraph<'channel>
where
Self: 'channel,
{
pub fn from_funding(funding: &'channel Funding) -> TxGraph<'channel> {
TxGraph {
funding,
cmt_version: 0,
cmt_locktime: 0,
cmt_sequence: 0,
cmt_outs: vec![],
graph: bmap! {},
}
}
pub fn tx<R, I>(&self, role: R, index: I) -> Option<&Psbt>
where
R: TxRole,
I: TxIndex,
{
self.graph
.get(&role.into())
.and_then(|v| v.get(&index.into()))
}
pub fn tx_mut<R, I>(&mut self, role: R, index: I) -> Option<&mut Psbt>
where
R: TxRole,
I: TxIndex,
{
self.graph
.get_mut(&role.into())
.and_then(|v| v.get_mut(&index.into()))
}
pub fn insert_tx<R, I>(
&mut self,
role: R,
index: I,
psbt: Psbt,
) -> Option<Psbt>
where
R: TxRole,
I: TxIndex,
{
self.graph
.entry(role.into())
.or_insert_with(Default::default)
.insert(index.into(), psbt)
}
pub fn len(&self) -> usize {
self.graph
.iter()
.fold(0usize, |sum, (_, map)| sum + map.len())
}
pub fn is_empty(&self) -> bool {
self.graph.len() == 0
}
pub fn last_index<R>(&self, role: R) -> usize
where
R: TxRole,
{
match self.graph.get(&role.into()) {
Some(map) => map.len(),
None => 0usize,
}
}
pub fn render(&self) -> Vec<Psbt> {
let mut txes = Vec::with_capacity(self.len());
let cmt_tx = self.render_cmt();
txes.push(cmt_tx);
txes.extend(self.graph.values().flat_map(|v| v.values().cloned()));
txes
}
pub fn render_cmt(&self) -> Psbt {
let cmt_tx = Transaction {
version: self.cmt_version,
lock_time: bitcoin::PackedLockTime(self.cmt_locktime),
input: vec![TxIn {
previous_output: self.funding.outpoint(),
script_sig: empty!(),
sequence: bitcoin::Sequence(self.cmt_sequence),
witness: empty!(),
}],
output: vec![default!(); self.cmt_outs.len()],
};
let mut psbt = Psbt::with(cmt_tx, PsbtVersion::V0).expect(
"PSBT construction fails only if script_sig and witness are not \
empty; which is not the case here",
);
let funding_psbt = self.funding.psbt();
let funding_vout = self.funding.output() as usize;
let funding_output = &funding_psbt.outputs[funding_vout];
psbt.inputs[0].witness_utxo = Some(TxOut {
value: funding_output.amount,
script_pubkey: funding_output.script.clone().into(),
});
psbt.inputs[0].witness_script = funding_output.witness_script.clone();
psbt.inputs[0].bip32_derivation =
funding_output.bip32_derivation.clone();
for (index, output) in psbt.outputs.iter_mut().enumerate() {
*output = self.cmt_outs[index].clone();
}
psbt
}
pub fn iter(&self) -> GraphIter {
GraphIter::with(self)
}
pub fn vec_mut(&mut self) -> Vec<(u16, u64, &mut Psbt)> {
let vec = self
.graph
.iter_mut()
.flat_map(|(role, map)| {
map.iter_mut().map(move |(index, tx)| (*role, *index, tx))
})
.collect::<Vec<_>>();
vec
}
}
pub struct GraphIter<'iter, 'channel> {
graph: &'iter TxGraph<'channel>,
curr_role: u16,
curr_index: u64,
}
impl<'iter, 'channel> GraphIter<'iter, 'channel> {
fn with(graph: &'iter TxGraph<'channel>) -> Self {
Self {
graph,
curr_role: 0,
curr_index: 0,
}
}
}
impl<'iter, 'channel> Iterator for GraphIter<'iter, 'channel> {
type Item = (u16, u64, &'iter Psbt);
fn next(&mut self) -> Option<Self::Item> {
let tx = self.graph.tx(self.curr_role, self.curr_index).or_else(|| {
self.curr_role += 1;
self.curr_index = 0;
self.graph.tx(self.curr_role, self.curr_index)
});
self.curr_index += 1;
tx.map(|tx| (self.curr_role, self.curr_index, tx))
}
}