namada_node/shell/
vote_extensions.rs

1//! Extend Tendermint votes with Ethereum bridge logic.
2
3pub mod bridge_pool_vext;
4pub mod eth_events;
5pub mod val_set_update;
6
7use drain_filter_polyfill::DrainFilter;
8use namada_sdk::eth_bridge::protocol::transactions::bridge_pool_roots::sign_bridge_pool_root;
9use namada_sdk::eth_bridge::protocol::transactions::ethereum_events::sign_ethereum_events;
10use namada_sdk::eth_bridge::protocol::transactions::validator_set_update::sign_validator_set_update;
11pub use namada_sdk::eth_bridge::protocol::validation::VoteExtensionError;
12use namada_sdk::tx::Signed;
13use namada_vote_ext::{
14    VoteExtension, bridge_pool_roots, ethereum_events, validator_set_update,
15};
16
17use super::*;
18use crate::shims::abcipp_shim_types::shim::TxBytes;
19
20/// Message to be passed to `.expect()` calls in this module.
21const VALIDATOR_EXPECT_MSG: &str = "Only validators receive this method call.";
22
23impl<D, H> Shell<D, H>
24where
25    D: DB + for<'iter> DBIter<'iter> + Sync + 'static,
26    H: StorageHasher + Sync + 'static,
27{
28    /// Creates the data to be added to a vote extension.
29    ///
30    /// INVARIANT: This method must be stateless.
31    #[inline]
32    pub fn craft_extension(&mut self) -> VoteExtension {
33        VoteExtension {
34            ethereum_events: self.extend_vote_with_ethereum_events(),
35            bridge_pool_root: self
36                .extend_vote_with_bp_roots()
37                .map(bridge_pool_roots::SignedVext),
38            validator_set_update: self.extend_vote_with_valset_update(),
39        }
40    }
41
42    /// Extend PreCommit votes with [`ethereum_events::Vext`] instances.
43    #[inline]
44    pub fn extend_vote_with_ethereum_events(
45        &mut self,
46    ) -> Option<Signed<ethereum_events::Vext>> {
47        let events = self.new_ethereum_events();
48        self.sign_ethereum_events(events)
49    }
50
51    /// Sign the given Ethereum events, and return the associated
52    /// vote extension protocol transaction.
53    pub fn sign_ethereum_events(
54        &self,
55        ethereum_events: Vec<EthereumEvent>,
56    ) -> Option<Signed<ethereum_events::Vext>> {
57        let validator_addr = self
58            .mode
59            .get_validator_address()
60            .expect(VALIDATOR_EXPECT_MSG);
61        let protocol_key = match &self.mode {
62            ShellMode::Validator { data, .. } => &data.keys.protocol_keypair,
63            _ => unreachable!("{VALIDATOR_EXPECT_MSG}"),
64        };
65        sign_ethereum_events(
66            &self.state,
67            validator_addr,
68            protocol_key,
69            ethereum_events,
70        )
71        .map(|ethereum_events::SignedVext(ext)| ext)
72    }
73
74    /// Extend PreCommit votes with [`bridge_pool_roots::Vext`] instances.
75    pub fn extend_vote_with_bp_roots(
76        &self,
77    ) -> Option<Signed<bridge_pool_roots::Vext>> {
78        let validator_addr = self
79            .mode
80            .get_validator_address()
81            .expect(VALIDATOR_EXPECT_MSG);
82        let eth_hot_key = self
83            .mode
84            .get_eth_bridge_keypair()
85            .expect(VALIDATOR_EXPECT_MSG);
86        let protocol_key = match &self.mode {
87            ShellMode::Validator { data, .. } => &data.keys.protocol_keypair,
88            _ => unreachable!("{VALIDATOR_EXPECT_MSG}"),
89        };
90        sign_bridge_pool_root(
91            &self.state,
92            validator_addr,
93            eth_hot_key,
94            protocol_key,
95        )
96        .map(|bridge_pool_roots::SignedVext(ext)| ext)
97    }
98
99    /// Extend PreCommit votes with [`validator_set_update::Vext`]
100    /// instances.
101    pub fn extend_vote_with_valset_update(
102        &self,
103    ) -> Option<validator_set_update::SignedVext> {
104        let validator_addr = self
105            .mode
106            .get_validator_address()
107            .expect(VALIDATOR_EXPECT_MSG);
108        let eth_hot_key = self
109            .mode
110            .get_eth_bridge_keypair()
111            .expect("{VALIDATOR_EXPECT_MSG}");
112        sign_validator_set_update::<_, _, governance::Store<_>>(
113            &self.state,
114            validator_addr,
115            eth_hot_key,
116        )
117    }
118
119    /// Given a slice of [`TxBytes`], return an iterator over the
120    /// ones we could deserialize to vote extension protocol txs.
121    pub fn deserialize_vote_extensions<'shell>(
122        &'shell self,
123        txs: &'shell mut Vec<TxBytes>,
124    ) -> DrainFilter<'shell, TxBytes, impl FnMut(&mut TxBytes) -> bool + 'shell>
125    {
126        drain_filter_polyfill::VecExt::drain_filter(txs, move |tx_bytes| {
127            let tx = match Tx::try_from_bytes(tx_bytes.as_ref()) {
128                Ok(tx) => tx,
129                Err(err) => {
130                    tracing::warn!(
131                        ?err,
132                        "Failed to deserialize tx in \
133                         deserialize_vote_extensions"
134                    );
135                    return false;
136                }
137            };
138            match (&tx).try_into().ok() {
139                Some(EthereumTxData::BridgePoolVext(_)) => true,
140                Some(EthereumTxData::EthEventsVext(ext)) => {
141                    // NB: only propose events with at least
142                    // one valid nonce
143                    ext.data.ethereum_events.iter().any(|event| {
144                        self.state
145                            .ethbridge_queries()
146                            .validate_eth_event_nonce(event)
147                    })
148                }
149                Some(EthereumTxData::ValSetUpdateVext(ext)) => {
150                    // only include non-stale validator set updates
151                    // in block proposals. it might be sitting long
152                    // enough in the mempool for it to no longer be
153                    // relevant to propose (e.g. a proof was constructed
154                    // before this validator set update got a chance
155                    // to be decided). unfortunately, we won't be able
156                    // to remove it from the mempool this way, but it
157                    // will eventually be evicted, getting replaced
158                    // by newer txs.
159                    let is_seen = self
160                        .state
161                        .ethbridge_queries()
162                        .valset_upd_seen(ext.data.signing_epoch.next());
163                    !is_seen
164                }
165                _ => false,
166            }
167        })
168    }
169}
170
171/// Yields an iterator over the protocol transactions
172/// in a [`VoteExtension`].
173pub fn iter_protocol_txs(
174    ext: VoteExtension,
175) -> impl Iterator<Item = EthereumTxData> {
176    let VoteExtension {
177        ethereum_events,
178        bridge_pool_root,
179        validator_set_update,
180    } = ext;
181    [
182        ethereum_events.map(|e| {
183            EthereumTxData::EthEventsVext(ethereum_events::SignedVext(e))
184        }),
185        bridge_pool_root.map(EthereumTxData::BridgePoolVext),
186        validator_set_update.map(EthereumTxData::ValSetUpdateVext),
187    ]
188    .into_iter()
189    .flatten()
190}