1use bitcoin::bip32::DerivationPath;
2use bitcoin::secp256k1::{PublicKey, SecretKey};
3use bitcoin::sighash::{EcdsaSighashType, SegwitV0Sighash};
4use bitcoin::{Network, ScriptBuf, Transaction};
5use lightning::ln::chan_utils::{ClosingTransaction, HTLCOutputInCommitment, TxCreationKeys};
6use lightning::sign::InMemorySigner;
7
8use super::filter::PolicyFilter;
9use super::simple_validator::SimpleValidatorFactory;
10use super::validator::EnforcementState;
11use super::validator::{ChainState, Validator, ValidatorFactory};
12use super::{policy_error_with_filter, Policy, DEFAULT_FEE_VELOCITY_CONTROL};
13use crate::channel::{ChannelId, ChannelSetup, ChannelSlot};
14use crate::prelude::*;
15use crate::tx::tx::{CommitmentInfo, CommitmentInfo2};
16use crate::util::velocity::VelocityControlSpec;
17use crate::wallet::Wallet;
18
19use crate::policy::temporary_policy_error_with_filter;
20use log::*;
21
22extern crate scopeguard;
23
24use super::error::ValidationError;
25
26pub struct OnchainValidatorFactory {
28 inner_factory: SimpleValidatorFactory,
29}
30
31impl OnchainValidatorFactory {
32 pub fn new() -> Self {
34 Self { inner_factory: SimpleValidatorFactory::new() }
35 }
36
37 pub fn new_with_simple_factory(inner_factory: SimpleValidatorFactory) -> Self {
39 Self { inner_factory }
40 }
41}
42
43impl ValidatorFactory for OnchainValidatorFactory {
44 fn make_validator(
45 &self,
46 network: Network,
47 node_id: PublicKey,
48 channel_id: Option<ChannelId>,
49 ) -> Arc<dyn Validator> {
50 let filter =
52 self.inner_factory.policy.as_ref().map(|p| p.filter.clone()).unwrap_or_default();
53 let validator = OnchainValidator {
54 inner: self.inner_factory.make_validator(network, node_id, channel_id),
55 policy: make_onchain_policy(network, filter),
56 };
57 Arc::new(validator)
58 }
59
60 fn policy(&self, network: Network) -> Box<dyn Policy> {
61 self.inner_factory.policy(network)
62 }
63}
64
65pub struct OnchainValidator {
67 inner: Arc<dyn Validator>,
68 policy: OnchainPolicy,
69}
70
71pub struct OnchainPolicy {
73 pub filter: PolicyFilter,
75 pub min_funding_depth: u16,
77}
78
79impl Policy for OnchainPolicy {
80 fn policy_error(&self, tag: String, msg: String) -> Result<(), ValidationError> {
81 policy_error_with_filter(tag, msg, &self.filter)
82 }
83
84 fn temporary_policy_error(&self, tag: String, msg: String) -> Result<(), ValidationError> {
85 temporary_policy_error_with_filter(tag, msg, &self.filter)
86 }
87
88 fn policy_log(&self, _tag: String, msg: String) {
89 error!("{}", msg);
90 }
91
92 fn global_velocity_control(&self) -> VelocityControlSpec {
93 VelocityControlSpec::UNLIMITED
94 }
95
96 fn fee_velocity_control(&self) -> VelocityControlSpec {
97 DEFAULT_FEE_VELOCITY_CONTROL
98 }
99}
100
101fn make_onchain_policy(_network: Network, filter: PolicyFilter) -> OnchainPolicy {
102 OnchainPolicy { filter, min_funding_depth: 1 }
103}
104
105impl Validator for OnchainValidator {
106 fn validate_setup_channel(
107 &self,
108 wallet: &dyn Wallet,
109 setup: &ChannelSetup,
110 holder_shutdown_key_path: &DerivationPath,
111 ) -> Result<(), ValidationError> {
112 self.inner.validate_setup_channel(wallet, setup, holder_shutdown_key_path)
113 }
114
115 fn validate_channel_value(&self, setup: &ChannelSetup) -> Result<(), ValidationError> {
116 self.inner.validate_channel_value(setup)
117 }
118
119 fn validate_onchain_tx(
120 &self,
121 wallet: &dyn Wallet,
122 channels: Vec<Option<Arc<Mutex<ChannelSlot>>>>,
123 tx: &Transaction,
124 segwit_flags: &[bool],
125 values_sat: &[u64],
126 opaths: &[DerivationPath],
127 weight: usize,
128 ) -> Result<u64, ValidationError> {
129 self.inner.validate_onchain_tx(
130 wallet,
131 channels,
132 tx,
133 segwit_flags,
134 values_sat,
135 opaths,
136 weight,
137 )
138 }
139
140 fn decode_commitment_tx(
141 &self,
142 keys: &InMemorySigner,
143 setup: &ChannelSetup,
144 is_counterparty: bool,
145 tx: &bitcoin::Transaction,
146 output_witscripts: &[Vec<u8>],
147 ) -> Result<CommitmentInfo, ValidationError> {
148 self.inner.decode_commitment_tx(keys, setup, is_counterparty, tx, output_witscripts)
150 }
151
152 fn validate_counterparty_commitment_tx(
153 &self,
154 estate: &EnforcementState,
155 commit_num: u64,
156 commitment_point: &PublicKey,
157 setup: &ChannelSetup,
158 cstate: &ChainState,
159 info2: &CommitmentInfo2,
160 ) -> Result<(), ValidationError> {
161 self.ensure_funding_buried_and_unspent(commit_num, cstate)?;
163 self.inner.validate_counterparty_commitment_tx(
164 estate,
165 commit_num,
166 commitment_point,
167 setup,
168 cstate,
169 info2,
170 )
171 }
172
173 fn validate_holder_commitment_tx(
174 &self,
175 estate: &EnforcementState,
176 commit_num: u64,
177 commitment_point: &PublicKey,
178 setup: &ChannelSetup,
179 cstate: &ChainState,
180 info2: &CommitmentInfo2,
181 ) -> Result<(), ValidationError> {
182 if estate.next_holder_commit_num <= commit_num {
184 self.ensure_funding_buried_and_unspent(commit_num, cstate)?;
185 }
186 self.inner.validate_holder_commitment_tx(
187 estate,
188 commit_num,
189 commitment_point,
190 setup,
191 cstate,
192 info2,
193 )
194 }
195
196 fn validate_counterparty_revocation(
197 &self,
198 state: &EnforcementState,
199 revoke_num: u64,
200 commitment_secret: &SecretKey,
201 ) -> Result<(), ValidationError> {
202 self.inner.validate_counterparty_revocation(state, revoke_num, commitment_secret)
203 }
204
205 fn decode_and_validate_htlc_tx(
208 &self,
209 is_counterparty: bool,
210 setup: &ChannelSetup,
211 txkeys: &TxCreationKeys,
212 tx: &Transaction,
213 redeemscript: &ScriptBuf,
214 htlc_amount_sat: u64,
215 output_witscript: &ScriptBuf,
216 ) -> Result<(u32, HTLCOutputInCommitment, SegwitV0Sighash, EcdsaSighashType), ValidationError>
217 {
218 self.inner.decode_and_validate_htlc_tx(
220 is_counterparty,
221 setup,
222 txkeys,
223 tx,
224 redeemscript,
225 htlc_amount_sat,
226 output_witscript,
227 )
228 }
229
230 fn validate_htlc_tx(
231 &self,
232 setup: &ChannelSetup,
233 cstate: &ChainState,
234 is_counterparty: bool,
235 htlc: &HTLCOutputInCommitment,
236 feerate_per_kw: u32,
237 ) -> Result<(), ValidationError> {
238 self.inner.validate_htlc_tx(setup, cstate, is_counterparty, htlc, feerate_per_kw)
239 }
240
241 fn decode_and_validate_mutual_close_tx(
242 &self,
243 wallet: &dyn Wallet,
244 setup: &ChannelSetup,
245 estate: &EnforcementState,
246 tx: &Transaction,
247 wallet_paths: &[DerivationPath],
248 ) -> Result<ClosingTransaction, ValidationError> {
249 self.inner.decode_and_validate_mutual_close_tx(wallet, setup, estate, tx, wallet_paths)
251 }
252
253 fn validate_mutual_close_tx(
254 &self,
255 wallet: &dyn Wallet,
256 setup: &ChannelSetup,
257 state: &EnforcementState,
258 to_holder_value_sat: u64,
259 to_counterparty_value_sat: u64,
260 holder_script: &Option<ScriptBuf>,
261 counterparty_script: &Option<ScriptBuf>,
262 holder_wallet_path_hint: &DerivationPath,
263 ) -> Result<(), ValidationError> {
264 self.inner.validate_mutual_close_tx(
265 wallet,
266 setup,
267 state,
268 to_holder_value_sat,
269 to_counterparty_value_sat,
270 holder_script,
271 counterparty_script,
272 holder_wallet_path_hint,
273 )
274 }
275
276 fn validate_delayed_sweep(
277 &self,
278 wallet: &dyn Wallet,
279 setup: &ChannelSetup,
280 cstate: &ChainState,
281 tx: &Transaction,
282 input: usize,
283 amount_sat: u64,
284 wallet_path: &DerivationPath,
285 ) -> Result<(), ValidationError> {
286 self.inner.validate_delayed_sweep(wallet, setup, cstate, tx, input, amount_sat, wallet_path)
287 }
288
289 fn validate_counterparty_htlc_sweep(
290 &self,
291 wallet: &dyn Wallet,
292 setup: &ChannelSetup,
293 cstate: &ChainState,
294 tx: &Transaction,
295 redeemscript: &ScriptBuf,
296 input: usize,
297 amount_sat: u64,
298 wallet_path: &DerivationPath,
299 ) -> Result<(), ValidationError> {
300 self.inner.validate_counterparty_htlc_sweep(
301 wallet,
302 setup,
303 cstate,
304 tx,
305 redeemscript,
306 input,
307 amount_sat,
308 wallet_path,
309 )
310 }
311
312 fn validate_justice_sweep(
313 &self,
314 wallet: &dyn Wallet,
315 setup: &ChannelSetup,
316 cstate: &ChainState,
317 tx: &Transaction,
318 input: usize,
319 amount_sat: u64,
320 wallet_path: &DerivationPath,
321 ) -> Result<(), ValidationError> {
322 self.inner.validate_justice_sweep(wallet, setup, cstate, tx, input, amount_sat, wallet_path)
323 }
324
325 fn validate_payment_balance(
326 &self,
327 incoming_msat: u64,
328 outgoing_msat: u64,
329 invoiced_amount_msat: Option<u64>,
330 ) -> Result<(), ValidationError> {
331 self.inner.validate_payment_balance(incoming_msat, outgoing_msat, invoiced_amount_msat)
332 }
333
334 fn minimum_initial_balance(&self, holder_value_msat: u64) -> u64 {
335 self.inner.minimum_initial_balance(holder_value_msat)
336 }
337
338 fn is_ready(&self, cstate: &ChainState) -> bool {
339 cstate.funding_depth >= self.policy.min_funding_depth as u32 && cstate.closing_depth == 0
340 }
341
342 fn policy(&self) -> Box<&dyn Policy> {
343 Box::new(&self.policy)
344 }
345}
346
347impl OnchainValidator {
348 fn ensure_funding_buried_and_unspent(
349 &self,
350 commit_num: u64,
351 cstate: &ChainState,
352 ) -> Result<(), ValidationError> {
353 debug!(
354 "ensure_funding_buried_and_unspent commit_num {} depth {} our height {}",
355 commit_num, cstate.funding_depth, cstate.current_height
356 );
357
358 if commit_num > 0 {
361 if cstate.funding_depth < self.policy.min_funding_depth as u32 {
362 temporary_policy_err!(
363 self,
364 "policy-commitment-spends-active-utxo",
365 "tried commitment {} when funding is not buried at depth {}, our height {}",
366 commit_num,
367 cstate.funding_depth,
368 cstate.current_height,
369 );
370 }
371
372 if cstate.closing_depth > 0 {
373 policy_err!(
374 self,
375 "policy-commitment-spends-active-utxo",
376 "tried commitment {} after closed on-chain at depth {}",
377 commit_num,
378 cstate.closing_depth
379 );
380 }
381 }
382 Ok(())
383 }
384}