1use bitcoin::util::psbt::raw::ProprietaryKey;
15use bitcoin::{OutPoint, Transaction, TxOut, Txid};
16use wallet::psbt::{Psbt, PsbtVersion};
17
18pub const PSBT_LNP_PROPRIETARY_PREFIX: &[u8] = b"LNP";
19pub const PSBT_OUT_LNP_CHANNEL_FUNDING: u8 = 0x01;
20
21#[derive(
22 Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error
23)]
24#[display(doc_comments)]
25pub enum Error {
26 NoFundingOutput,
30
31 WrongOutput(u16),
34}
35
36#[derive(Getters, Clone, PartialEq, Eq, Debug, StrictEncode, StrictDecode)]
38#[cfg_attr(
39 feature = "serde",
40 derive(Serialize, Deserialize),
41 serde(crate = "serde_crate")
42)]
43pub struct Funding {
44 psbt: Psbt,
52
53 #[getter(as_copy)]
55 txid: Txid,
56
57 #[getter(as_copy)]
58 output: u16,
59
60 #[getter(as_copy)]
61 amount: u64,
62
63 #[getter(as_copy)]
64 signing_parties: u8,
65
66 #[getter(as_copy)]
67 signing_threshold: u8,
68}
69
70impl Funding {
71 #[inline]
74 pub(super) fn new() -> Funding {
75 let mut psbt = Psbt::with(
76 Transaction {
77 version: 2,
78 lock_time: bitcoin::PackedLockTime(0),
79 input: vec![],
80 output: vec![TxOut {
81 value: 0,
82 script_pubkey: Default::default(),
83 }],
84 },
85 PsbtVersion::V0,
86 )
87 .expect("dumb manual PSBT creation");
88 psbt.outputs[0]
89 .proprietary
90 .insert(lnp_out_channel_funding_key(), vec![]);
91 Funding::with(psbt).expect("dumb manual PSBT creation")
92 }
93
94 #[inline]
95 pub fn with(psbt: Psbt) -> Result<Funding, Error> {
96 psbt.extract_channel_funding()
97 }
98
99 #[inline]
100 pub(super) fn preliminary(funding_amount: u64) -> Funding {
101 let mut funding = Funding::new();
102 funding.amount = funding_amount;
103 funding
104 }
105
106 #[inline]
107 pub fn outpoint(&self) -> OutPoint {
108 OutPoint::new(self.txid, self.output as u32)
109 }
110}
111
112fn lnp_out_channel_funding_key() -> ProprietaryKey {
113 ProprietaryKey {
114 prefix: PSBT_LNP_PROPRIETARY_PREFIX.to_vec(),
115 subtype: PSBT_OUT_LNP_CHANNEL_FUNDING,
116 key: vec![],
117 }
118}
119
120pub trait PsbtLnpFunding {
121 fn channel_funding_output(&self) -> Option<usize>;
122 fn set_channel_funding_output(&mut self, vout: u16) -> Result<(), Error>;
123 fn channel_funding_outpoint(&self) -> Result<OutPoint, Error>;
124 fn extract_channel_funding(self) -> Result<Funding, Error>;
125}
126
127impl PsbtLnpFunding for Psbt {
128 fn channel_funding_output(&self) -> Option<usize> {
129 let funding_key = lnp_out_channel_funding_key();
130 self.outputs
131 .iter()
132 .enumerate()
133 .find(|(_, output)| output.proprietary.get(&funding_key).is_some())
134 .map(|(index, _)| index)
135 }
136
137 fn set_channel_funding_output(&mut self, vout: u16) -> Result<(), Error> {
138 self.outputs
139 .get_mut(vout as usize)
140 .map(|out| {
141 out.proprietary
142 .insert(lnp_out_channel_funding_key(), vec![]);
143 })
144 .ok_or(Error::WrongOutput(vout))
145 }
146
147 fn channel_funding_outpoint(&self) -> Result<OutPoint, Error> {
148 let vout = self
149 .channel_funding_output()
150 .ok_or(Error::NoFundingOutput)?;
151 Ok(OutPoint::new(self.to_txid(), vout as u32))
152 }
153
154 fn extract_channel_funding(self) -> Result<Funding, Error> {
155 let vout = self
156 .channel_funding_output()
157 .ok_or(Error::NoFundingOutput)?;
158 let amount = self.outputs[vout].amount;
159 let txid = self.to_txid();
160 Ok(Funding {
163 psbt: self,
164 txid,
165 output: vout as u16,
166 amount,
167 signing_parties: 2,
168 signing_threshold: 2,
169 })
170 }
171}