vls_proxy/
nodefront.rs

1//! The SignerFront and NodeFront provide a in-process call interface to the underlying MultiSigner
2//! and Node objects for the ChainTrack traits.
3
4use std::sync::Arc;
5
6use async_trait::async_trait;
7
8use bitcoin::blockdata::block::Header as BlockHeader;
9use bitcoin::consensus::serialize;
10use bitcoin::secp256k1::PublicKey;
11use bitcoin::{BlockHash, Network, OutPoint, Txid};
12
13use crate::persist::ExternalPersistWithHelper;
14use lightning_signer::bitcoin;
15use lightning_signer::chain::tracker::{ChainTracker, Headers};
16use lightning_signer::monitor::ChainMonitor;
17use lightning_signer::node::{Node, SignedHeartbeat};
18use lightning_signer::persist::Persist;
19use lightning_signer::signer::multi_signer::MultiSigner;
20use lightning_signer::txoo::proof::TxoProof;
21use lightning_signer::wallet::Wallet;
22use vls_frontend::{ChainTrack, ChainTrackDirectory};
23
24/// Implements ChainTrackDirectory using calls to inplace MultiSigner
25pub struct SignerFront {
26    /// The signer
27    pub signer: Arc<MultiSigner>,
28    /// Optional external persister
29    pub external_persist: Option<ExternalPersistWithHelper>,
30}
31
32#[async_trait]
33impl ChainTrackDirectory for SignerFront {
34    fn tracker(&self, node_id: &PublicKey) -> Arc<dyn ChainTrack> {
35        let node = self.signer.get_node(node_id).unwrap();
36        Arc::new(NodeFront::new(node, self.external_persist.clone()))
37    }
38    async fn trackers(&self) -> Vec<Arc<dyn ChainTrack>> {
39        self.signer.get_node_ids().iter().map(|node_id| self.tracker(node_id)).collect()
40    }
41}
42
43/// Implements ChainTrackDirectory using single inplace node
44pub struct SingleFront {
45    pub node: Arc<Node>,
46    pub external_persist: Option<ExternalPersistWithHelper>,
47}
48
49#[async_trait]
50impl ChainTrackDirectory for SingleFront {
51    fn tracker(&self, _node_id: &PublicKey) -> Arc<dyn ChainTrack> {
52        // There are no additional added trackers for new nodes in the single case.
53        unimplemented!();
54    }
55    async fn trackers(&self) -> Vec<Arc<dyn ChainTrack>> {
56        vec![Arc::new(NodeFront::new(Arc::clone(&self.node), self.external_persist.clone()))]
57    }
58}
59
60/// Implements ChainTrack using calls to inplace node
61pub(crate) struct NodeFront {
62    node: Arc<Node>,
63    heartbeat_pubkey: PublicKey,
64    external_persist: Option<ExternalPersistWithHelper>,
65}
66
67impl NodeFront {
68    pub fn new(node: Arc<Node>, external_persist: Option<ExternalPersistWithHelper>) -> Self {
69        let heartbeat_pubkey = node.get_account_extended_pubkey().public_key.clone();
70        Self { node, heartbeat_pubkey, external_persist }
71    }
72
73    fn do_add_block(&self, header: BlockHeader, proof: TxoProof, persister: Arc<dyn Persist>) {
74        let mut tracker = self.node.get_tracker();
75        let proof = self.maybe_stream_block(&mut *tracker, proof);
76        tracker
77            .add_block(header, proof)
78            .unwrap_or_else(|e| panic!("{}: add_block failed: {:?}", self.node.log_prefix(), e));
79        persister.update_tracker(&self.node.get_id(), &tracker).unwrap_or_else(|e| {
80            panic!("{}: persist tracker failed: {:?}", self.node.log_prefix(), e)
81        });
82    }
83
84    fn do_remove_block(&self, proof: TxoProof, persister: Arc<dyn Persist>, prev_headers: Headers) {
85        let mut tracker = self.node.get_tracker();
86        let proof = self.maybe_stream_block(&mut *tracker, proof);
87        tracker
88            .remove_block(proof, prev_headers)
89            .unwrap_or_else(|e| panic!("{}: remove_block failed: {:?}", self.node.log_prefix(), e));
90        persister.update_tracker(&self.node.get_id(), &tracker).unwrap_or_else(|e| {
91            panic!("{}: persist tracker failed: {:?}", self.node.log_prefix(), e)
92        });
93    }
94
95    fn maybe_stream_block(
96        &self,
97        tracker: &mut ChainTracker<ChainMonitor>,
98        proof: TxoProof,
99    ) -> TxoProof {
100        // stream the block to the signer, if this is a false positive
101        let (proof, block_opt) = proof.take_block();
102        if let Some(block) = block_opt {
103            let block_hash = block.block_hash();
104            let bytes = serialize(&block);
105            let mut offset = 0;
106            // small prime chunk size to test streaming
107            for chunk in bytes.chunks(23) {
108                tracker.block_chunk(block_hash, offset, chunk).expect("block_chunk");
109                offset += chunk.len() as u32;
110            }
111        }
112        proof
113    }
114
115    fn do_beat(&self, _persister: Arc<dyn Persist>) -> SignedHeartbeat {
116        self.node.get_heartbeat()
117    }
118
119    async fn with_persist_context<F>(
120        external_persist: &ExternalPersistWithHelper,
121        persister: Arc<dyn Persist>,
122        f: F,
123    ) where
124        F: FnOnce(Arc<dyn Persist>),
125    {
126        // lock order: persist client, tracker
127        let client = external_persist.persist_client.lock().await;
128
129        persister.enter().expect("persister enter");
130        f(persister.clone());
131        let muts = persister.prepare();
132
133        let helper = &external_persist.helper;
134        let client_hmac = helper.client_hmac(&muts);
135        let server_hmac = helper.server_hmac(&muts);
136        let received_server_hmac =
137            client.put(muts.clone(), &client_hmac).await.expect("persist failed");
138        assert_eq!(received_server_hmac, server_hmac, "server hmac mismatch");
139
140        persister.commit().expect("persister commit")
141    }
142}
143
144#[async_trait]
145impl ChainTrack for NodeFront {
146    fn log_prefix(&self) -> String {
147        format!("tracker {}", self.node.log_prefix())
148    }
149
150    async fn id(&self) -> Vec<u8> {
151        self.node.get_id().serialize().to_vec()
152    }
153
154    async fn heartbeat_pubkey(&self) -> PublicKey {
155        self.heartbeat_pubkey.clone()
156    }
157
158    fn network(&self) -> Network {
159        self.node.network()
160    }
161
162    async fn tip_info(&self) -> (u32, BlockHash) {
163        let tracker = self.node.get_tracker();
164        (tracker.height(), tracker.tip().0.block_hash())
165    }
166
167    async fn forward_watches(&self) -> (Vec<Txid>, Vec<OutPoint>) {
168        self.node.get_tracker().get_all_forward_watches()
169    }
170
171    async fn reverse_watches(&self) -> (Vec<Txid>, Vec<OutPoint>) {
172        self.node.get_tracker().get_all_reverse_watches()
173    }
174
175    async fn add_block(&self, header: BlockHeader, proof: TxoProof) {
176        let persister = self.node.get_persister();
177        if let Some(external_persist) = &self.external_persist {
178            Self::with_persist_context(external_persist, persister, |persister| {
179                self.do_add_block(header, proof, persister);
180            })
181            .await;
182        } else {
183            self.do_add_block(header, proof, persister);
184        }
185    }
186
187    async fn remove_block(&self, proof: TxoProof, prev_headers: Headers) {
188        let persister = self.node.get_persister();
189        if let Some(external_persist) = &self.external_persist {
190            Self::with_persist_context(external_persist, persister, |persister| {
191                self.do_remove_block(proof, persister, prev_headers);
192            })
193            .await;
194        } else {
195            self.do_remove_block(proof, persister, prev_headers);
196        }
197    }
198
199    async fn beat(&self) -> SignedHeartbeat {
200        let persister = self.node.get_persister();
201        let mut beat: Option<SignedHeartbeat> = None;
202        if let Some(external_persist) = &self.external_persist {
203            Self::with_persist_context(external_persist, persister, |persister| {
204                beat = Some(self.do_beat(persister));
205            })
206            .await
207        } else {
208            beat = Some(self.do_beat(persister));
209        }
210        beat.unwrap()
211    }
212}