lnp/channel/
tx_graph.rs

1// LNP/BP Core Library implementing LNPBP specifications & standards
2// Written in 2020-2022 by
3//     Dr. Maxim Orlovsky <orlovsky@pandoracore.com>
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the MIT License
11// along with this software.
12// If not, see <https://opensource.org/licenses/MIT>.
13
14//! The module must be used only by libraries providing new channel types and
15//! not by the final LN node implementations.
16
17use std::collections::BTreeMap;
18
19use bitcoin::{Transaction, TxIn, TxOut};
20use wallet::psbt::{self, Psbt, PsbtVersion};
21
22use crate::channel::Funding;
23
24pub trait TxRole: Clone + From<u16> + Into<u16> {}
25pub trait TxIndex: Clone + From<u64> + Into<u64> {}
26
27impl TxRole for u16 {}
28impl TxIndex for u64 {}
29
30#[derive(Getters, Clone, Eq, PartialEq)]
31pub struct TxGraph<'channel> {
32    /// Read-only data for extensions on the number of channel parties
33    funding: &'channel Funding,
34    pub cmt_version: i32,
35    pub cmt_locktime: u32,
36    pub cmt_sequence: u32,
37    pub cmt_outs: Vec<psbt::Output>,
38    graph: BTreeMap<u16, BTreeMap<u64, Psbt>>,
39}
40
41impl<'channel> TxGraph<'channel>
42where
43    Self: 'channel,
44{
45    pub fn from_funding(funding: &'channel Funding) -> TxGraph<'channel> {
46        TxGraph {
47            funding,
48            // TODO: Check that we have commitment version set correctly
49            cmt_version: 0,
50            cmt_locktime: 0,
51            cmt_sequence: 0,
52            cmt_outs: vec![],
53            graph: bmap! {},
54        }
55    }
56
57    pub fn tx<R, I>(&self, role: R, index: I) -> Option<&Psbt>
58    where
59        R: TxRole,
60        I: TxIndex,
61    {
62        self.graph
63            .get(&role.into())
64            .and_then(|v| v.get(&index.into()))
65    }
66
67    pub fn tx_mut<R, I>(&mut self, role: R, index: I) -> Option<&mut Psbt>
68    where
69        R: TxRole,
70        I: TxIndex,
71    {
72        self.graph
73            .get_mut(&role.into())
74            .and_then(|v| v.get_mut(&index.into()))
75    }
76
77    pub fn insert_tx<R, I>(
78        &mut self,
79        role: R,
80        index: I,
81        psbt: Psbt,
82    ) -> Option<Psbt>
83    where
84        R: TxRole,
85        I: TxIndex,
86    {
87        self.graph
88            .entry(role.into())
89            .or_insert_with(Default::default)
90            .insert(index.into(), psbt)
91    }
92
93    pub fn len(&self) -> usize {
94        self.graph
95            .iter()
96            .fold(0usize, |sum, (_, map)| sum + map.len())
97    }
98
99    pub fn is_empty(&self) -> bool {
100        self.graph.len() == 0
101    }
102
103    pub fn last_index<R>(&self, role: R) -> usize
104    where
105        R: TxRole,
106    {
107        match self.graph.get(&role.into()) {
108            Some(map) => map.len(),
109            None => 0usize,
110        }
111    }
112
113    pub fn render(&self) -> Vec<Psbt> {
114        let mut txes = Vec::with_capacity(self.len());
115        let cmt_tx = self.render_cmt();
116        txes.push(cmt_tx);
117        txes.extend(self.graph.values().flat_map(|v| v.values().cloned()));
118        txes
119    }
120
121    pub fn render_cmt(&self) -> Psbt {
122        let cmt_tx = Transaction {
123            version: self.cmt_version,
124            lock_time: bitcoin::PackedLockTime(self.cmt_locktime),
125            input: vec![TxIn {
126                previous_output: self.funding.outpoint(),
127                script_sig: empty!(),
128                sequence: bitcoin::Sequence(self.cmt_sequence),
129                witness: empty!(),
130            }],
131            output: vec![default!(); self.cmt_outs.len()],
132        };
133        let mut psbt = Psbt::with(cmt_tx, PsbtVersion::V0).expect(
134            "PSBT construction fails only if script_sig and witness are not \
135             empty; which is not the case here",
136        );
137        let funding_psbt = self.funding.psbt();
138        let funding_vout = self.funding.output() as usize;
139        let funding_output = &funding_psbt.outputs[funding_vout];
140        psbt.inputs[0].witness_utxo = Some(TxOut {
141            value: funding_output.amount,
142            script_pubkey: funding_output.script.clone().into(),
143        });
144        psbt.inputs[0].witness_script = funding_output.witness_script.clone();
145        psbt.inputs[0].bip32_derivation =
146            funding_output.bip32_derivation.clone();
147        for (index, output) in psbt.outputs.iter_mut().enumerate() {
148            *output = self.cmt_outs[index].clone();
149        }
150        psbt
151    }
152
153    pub fn iter(&self) -> GraphIter {
154        GraphIter::with(self)
155    }
156
157    pub fn vec_mut(&mut self) -> Vec<(u16, u64, &mut Psbt)> {
158        let vec = self
159            .graph
160            .iter_mut()
161            .flat_map(|(role, map)| {
162                map.iter_mut().map(move |(index, tx)| (*role, *index, tx))
163            })
164            .collect::<Vec<_>>();
165        vec
166    }
167}
168
169pub struct GraphIter<'iter, 'channel> {
170    graph: &'iter TxGraph<'channel>,
171    curr_role: u16,
172    curr_index: u64,
173}
174
175impl<'iter, 'channel> GraphIter<'iter, 'channel> {
176    fn with(graph: &'iter TxGraph<'channel>) -> Self {
177        Self {
178            graph,
179            curr_role: 0,
180            curr_index: 0,
181        }
182    }
183}
184
185impl<'iter, 'channel> Iterator for GraphIter<'iter, 'channel> {
186    type Item = (u16, u64, &'iter Psbt);
187
188    fn next(&mut self) -> Option<Self::Item> {
189        let tx = self.graph.tx(self.curr_role, self.curr_index).or_else(|| {
190            self.curr_role += 1;
191            self.curr_index = 0;
192            self.graph.tx(self.curr_role, self.curr_index)
193        });
194        self.curr_index += 1;
195        tx.map(|tx| (self.curr_role, self.curr_index, tx))
196    }
197}