hopr_db_sql/
resolver.rs

1use async_trait::async_trait;
2use hopr_crypto_types::types::OffchainPublicKey;
3use hopr_db_api::{
4    errors::{DbError, Result},
5    resolver::HoprDbResolverOperations,
6};
7use hopr_primitive_types::{primitives::Address, traits::ToHex};
8use multiaddr::PeerId;
9
10use crate::{accounts::HoprDbAccountOperations, db::HoprDb};
11
12#[async_trait]
13impl HoprDbResolverOperations for HoprDb {
14    #[tracing::instrument(level = "trace", skip_all, fields(onchain_key = onchain_key.to_hex().as_str()), err)]
15    async fn resolve_packet_key(&self, onchain_key: &Address) -> Result<Option<OffchainPublicKey>> {
16        Ok(self
17            .translate_key(None, *onchain_key)
18            .await?
19            .map(|k| k.try_into())
20            .inspect(|v: &std::result::Result<OffchainPublicKey, _>| {
21                if let Ok(offchain_pk) = v {
22                    tracing::trace!(
23                        peer = %Into::<PeerId>::into(offchain_pk),
24                        offchain_pk = offchain_pk.to_hex(),
25                        "found offchain key",
26                    );
27                }
28            })
29            .transpose()
30            .map_err(|_e| DbError::LogicalError("failed to transpose the translated key".into()))?)
31    }
32
33    #[tracing::instrument(level = "trace", skip_all, fields(offchain_key = %Into::<PeerId>::into(offchain_key)), ret, err)]
34    async fn resolve_chain_key(&self, offchain_key: &OffchainPublicKey) -> Result<Option<Address>> {
35        Ok(self
36            .translate_key(None, *offchain_key)
37            .await?
38            .map(|k| k.try_into())
39            .transpose()
40            .map_err(|_e| DbError::LogicalError("failed to transpose the translated key".into()))?)
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use hopr_crypto_types::prelude::{ChainKeypair, Keypair, OffchainKeypair};
47    use hopr_internal_types::account::{AccountEntry, AccountType};
48    use hopr_primitive_types::prelude::ToHex;
49    use sea_orm::{EntityTrait, Set};
50
51    use super::*;
52
53    #[tokio::test]
54    async fn test_get_offchain_key_should_return_nothing_if_a_mapping_to_chain_key_does_not_exist() -> anyhow::Result<()>
55    {
56        let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
57
58        let chain = ChainKeypair::random().public().to_address();
59
60        let actual_pk = db.resolve_packet_key(&chain).await?;
61        assert_eq!(actual_pk, None, "offchain key should not be present");
62        Ok(())
63    }
64
65    #[tokio::test]
66    async fn test_get_chain_key_should_return_nothing_if_a_mapping_to_offchain_key_does_not_exist() -> anyhow::Result<()>
67    {
68        let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
69
70        let packet = *OffchainKeypair::random().public();
71
72        let actual_ck = db.resolve_chain_key(&packet).await?;
73        assert_eq!(actual_ck, None, "chain key should not be present");
74        Ok(())
75    }
76
77    #[tokio::test]
78    async fn test_get_chain_key_should_succeed_if_a_mapping_to_offchain_key_exists() -> anyhow::Result<()> {
79        let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
80
81        // Inserting to the table directly to avoid cache
82
83        let chain_1 = ChainKeypair::random().public().to_address();
84        let packet_1 = *OffchainKeypair::random().public();
85        let account_1 = hopr_db_entity::account::ActiveModel {
86            chain_key: Set(chain_1.to_hex()),
87            packet_key: Set(packet_1.to_hex()),
88            ..Default::default()
89        };
90
91        let chain_2 = ChainKeypair::random().public().to_address();
92        let packet_2 = *OffchainKeypair::random().public();
93        let account_2 = hopr_db_entity::account::ActiveModel {
94            chain_key: Set(chain_2.to_hex()),
95            packet_key: Set(packet_2.to_hex()),
96            ..Default::default()
97        };
98
99        hopr_db_entity::account::Entity::insert_many([account_1, account_2])
100            .exec(db.index_db.read_write())
101            .await?;
102
103        let actual_ck = db.resolve_chain_key(&packet_1).await?;
104        assert_eq!(actual_ck, Some(chain_1), "chain keys must match");
105        Ok(())
106    }
107
108    #[tokio::test]
109    async fn test_get_chain_key_should_succeed_if_a_mapping_to_offchain_key_exists_with_cache() -> anyhow::Result<()> {
110        let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
111
112        // Inserting to the table via API to insert into cache as well
113
114        let chain_1 = ChainKeypair::random().public().to_address();
115        let packet_1 = *OffchainKeypair::random().public();
116        db.insert_account(
117            None,
118            AccountEntry {
119                public_key: packet_1,
120                chain_addr: chain_1,
121                entry_type: AccountType::NotAnnounced,
122                published_at: 1,
123            },
124        )
125        .await?;
126
127        let chain_2 = ChainKeypair::random().public().to_address();
128        let packet_2 = *OffchainKeypair::random().public();
129        db.insert_account(
130            None,
131            AccountEntry {
132                public_key: packet_2,
133                chain_addr: chain_2,
134                entry_type: AccountType::NotAnnounced,
135                published_at: 1,
136            },
137        )
138        .await?;
139
140        let actual_ck = db.resolve_chain_key(&packet_1).await?;
141        assert_eq!(actual_ck, Some(chain_1), "chain keys must match");
142        Ok(())
143    }
144
145    #[tokio::test]
146    async fn test_get_offchain_key_should_succeed_if_a_mapping_to_chain_key_exists() -> anyhow::Result<()> {
147        let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
148
149        // Inserting to the table directly to avoid cache
150
151        let chain_1 = ChainKeypair::random().public().to_address();
152        let packet_1 = *OffchainKeypair::random().public();
153        let account_1 = hopr_db_entity::account::ActiveModel {
154            chain_key: Set(chain_1.to_hex()),
155            packet_key: Set(packet_1.to_hex()),
156            ..Default::default()
157        };
158
159        let chain_2 = ChainKeypair::random().public().to_address();
160        let packet_2 = *OffchainKeypair::random().public();
161        let account_2 = hopr_db_entity::account::ActiveModel {
162            chain_key: Set(chain_2.to_hex()),
163            packet_key: Set(packet_2.to_hex()),
164            ..Default::default()
165        };
166
167        hopr_db_entity::account::Entity::insert_many([account_1, account_2])
168            .exec(db.index_db.read_write())
169            .await?;
170
171        let actual_pk = db.resolve_packet_key(&chain_2).await?;
172
173        assert_eq!(actual_pk, Some(packet_2), "packet keys must match");
174        Ok(())
175    }
176
177    #[tokio::test]
178    async fn test_get_offchain_key_should_succeed_if_a_mapping_to_chain_key_exists_with_cache() -> anyhow::Result<()> {
179        let db = HoprDb::new_in_memory(ChainKeypair::random()).await?;
180
181        // Inserting to the table via API to insert into cache as well
182
183        let chain_1 = ChainKeypair::random().public().to_address();
184        let packet_1 = *OffchainKeypair::random().public();
185        db.insert_account(
186            None,
187            AccountEntry {
188                public_key: packet_1,
189                chain_addr: chain_1,
190                entry_type: AccountType::NotAnnounced,
191                published_at: 1,
192            },
193        )
194        .await?;
195
196        let chain_2 = ChainKeypair::random().public().to_address();
197        let packet_2 = *OffchainKeypair::random().public();
198        db.insert_account(
199            None,
200            AccountEntry {
201                public_key: packet_2,
202                chain_addr: chain_2,
203                entry_type: AccountType::NotAnnounced,
204                published_at: 1,
205            },
206        )
207        .await?;
208
209        let actual_pk = db.resolve_packet_key(&chain_2).await?;
210
211        assert_eq!(actual_pk, Some(packet_2), "packet keys must match");
212        Ok(())
213    }
214}