1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! The SignerFront and NodeFront provide a in-process call interface to the underlying MultiSigner
//! and Node objects for the ChainTrack traits.

use std::sync::Arc;

use async_trait::async_trait;

use bitcoin::secp256k1::PublicKey;
use bitcoin::{BlockHash, BlockHeader, Network, OutPoint, Txid};

use crate::persist::ExternalPersistWithHelper;
use lightning_signer::bitcoin;
use lightning_signer::node::{Node, SignedHeartbeat};
use lightning_signer::persist::Persist;
use lightning_signer::signer::multi_signer::MultiSigner;
use lightning_signer::txoo::proof::TxoProof;
use lightning_signer::wallet::Wallet;
use vls_frontend::{ChainTrack, ChainTrackDirectory};

/// Implements ChainTrackDirectory using calls to inplace MultiSigner
pub struct SignerFront {
    /// The signer
    pub signer: Arc<MultiSigner>,
    /// Optional external persister
    pub external_persist: Option<ExternalPersistWithHelper>,
}

#[async_trait]
impl ChainTrackDirectory for SignerFront {
    fn tracker(&self, node_id: &PublicKey) -> Arc<dyn ChainTrack> {
        let node = self.signer.get_node(node_id).unwrap();
        Arc::new(NodeFront::new(node, self.external_persist.clone()))
    }
    async fn trackers(&self) -> Vec<Arc<dyn ChainTrack>> {
        self.signer.get_node_ids().iter().map(|node_id| self.tracker(node_id)).collect()
    }
}

/// Implements ChainTrackDirectory using single inplace node
pub struct SingleFront {
    pub node: Arc<Node>,
    pub external_persist: Option<ExternalPersistWithHelper>,
}

#[async_trait]
impl ChainTrackDirectory for SingleFront {
    fn tracker(&self, _node_id: &PublicKey) -> Arc<dyn ChainTrack> {
        // There are no additional added trackers for new nodes in the single case.
        unimplemented!();
    }
    async fn trackers(&self) -> Vec<Arc<dyn ChainTrack>> {
        vec![Arc::new(NodeFront::new(Arc::clone(&self.node), self.external_persist.clone()))]
    }
}

/// Implements ChainTrack using calls to inplace node
pub(crate) struct NodeFront {
    node: Arc<Node>,
    heartbeat_pubkey: PublicKey,
    external_persist: Option<ExternalPersistWithHelper>,
}

impl NodeFront {
    pub fn new(node: Arc<Node>, external_persist: Option<ExternalPersistWithHelper>) -> Self {
        let heartbeat_pubkey = node.get_account_extended_pubkey().public_key.clone();
        Self { node, heartbeat_pubkey, external_persist }
    }

    fn do_add_block(&self, header: BlockHeader, proof: TxoProof, persister: Arc<dyn Persist>) {
        let mut tracker = self.node.get_tracker();
        tracker
            .add_block(header, proof)
            .unwrap_or_else(|e| panic!("{}: add_block failed: {:?}", self.node.log_prefix(), e));
        persister.update_tracker(&self.node.get_id(), &tracker).unwrap_or_else(|e| {
            panic!("{}: persist tracker failed: {:?}", self.node.log_prefix(), e)
        });
    }

    fn do_remove_block(&self, proof: TxoProof, persister: Arc<dyn Persist>) {
        let mut tracker = self.node.get_tracker();
        tracker
            .remove_block(proof)
            .unwrap_or_else(|e| panic!("{}: remove_block failed: {:?}", self.node.log_prefix(), e));
        persister.update_tracker(&self.node.get_id(), &tracker).unwrap_or_else(|e| {
            panic!("{}: persist tracker failed: {:?}", self.node.log_prefix(), e)
        });
    }

    async fn with_persist_context<F>(
        external_persist: &ExternalPersistWithHelper,
        persister: Arc<dyn Persist>,
        f: F,
    ) where
        F: FnOnce(Arc<dyn Persist>),
    {
        // lock order: persist client, tracker
        let client = external_persist.persist_client.lock().await;

        let muts = {
            let context = persister.enter(external_persist.state.clone());
            f(persister);
            context.exit()
        };

        let helper = &external_persist.helper;
        let client_hmac = helper.client_hmac(&muts);
        let server_hmac = helper.server_hmac(&muts);
        let received_server_hmac =
            client.put(muts.clone(), &client_hmac).await.expect("persist failed");
        assert_eq!(received_server_hmac, server_hmac, "server hmac mismatch");
    }
}

#[async_trait]
impl ChainTrack for NodeFront {
    fn log_prefix(&self) -> String {
        format!("tracker {}", self.node.log_prefix())
    }

    async fn id(&self) -> Vec<u8> {
        self.node.get_id().serialize().to_vec()
    }

    async fn heartbeat_pubkey(&self) -> PublicKey {
        self.heartbeat_pubkey.clone()
    }

    fn network(&self) -> Network {
        self.node.network()
    }

    async fn tip_info(&self) -> (u32, BlockHash) {
        let tracker = self.node.get_tracker();
        (tracker.height(), tracker.tip().0.block_hash())
    }

    async fn forward_watches(&self) -> (Vec<Txid>, Vec<OutPoint>) {
        self.node.get_tracker().get_all_forward_watches()
    }

    async fn reverse_watches(&self) -> (Vec<Txid>, Vec<OutPoint>) {
        self.node.get_tracker().get_all_reverse_watches()
    }

    async fn add_block(&self, header: BlockHeader, proof: TxoProof) {
        let persister = self.node.get_persister();
        if let Some(external_persist) = &self.external_persist {
            Self::with_persist_context(external_persist, persister, |persister| {
                self.do_add_block(header, proof, persister);
            })
            .await;
        } else {
            self.do_add_block(header, proof, persister);
        }
    }

    async fn remove_block(&self, proof: TxoProof) {
        let persister = self.node.get_persister();
        if let Some(external_persist) = &self.external_persist {
            Self::with_persist_context(external_persist, persister, |persister| {
                self.do_remove_block(proof, persister);
            })
            .await;
        } else {
            self.do_remove_block(proof, persister);
        }
    }

    async fn beat(&self) -> SignedHeartbeat {
        self.node.get_heartbeat()
    }
}