1use std::sync::Arc;
2
3use async_trait::async_trait;
4use bsv::remittance::CommsLayer;
5use bsv::remittance::types::PeerMessage;
6use bsv::remittance::error::RemittanceError;
7use bsv::wallet::interfaces::WalletInterface;
8
9use crate::client::MessageBoxClient;
10
11pub struct RemittanceAdapter<W: WalletInterface + Clone + 'static> {
21 inner: Arc<MessageBoxClient<W>>,
22}
23
24impl<W: WalletInterface + Clone + 'static> RemittanceAdapter<W> {
25 pub fn new(client: Arc<MessageBoxClient<W>>) -> Self {
27 Self { inner: client }
28 }
29}
30
31#[async_trait]
32impl<W: WalletInterface + Clone + 'static + Send + Sync> CommsLayer for RemittanceAdapter<W> {
33 async fn send_message(
39 &self,
40 recipient: &str,
41 message_box: &str,
42 body: &str,
43 host_override: Option<&str>,
44 ) -> Result<String, RemittanceError> {
45 match host_override {
46 Some(host) => self.inner
47 .send_message_to_host(host, recipient, message_box, body, false, false, None, None)
48 .await
49 .map_err(|e| RemittanceError::Protocol(e.to_string())),
50 None => self.inner
51 .send_message(recipient, message_box, body, false, false, None, None)
52 .await
53 .map_err(|e| RemittanceError::Protocol(e.to_string())),
54 }
55 }
56
57 async fn list_messages(
63 &self,
64 message_box: &str,
65 _host: Option<&str>,
66 ) -> Result<Vec<PeerMessage>, RemittanceError> {
67 let identity_key = self
69 .inner
70 .get_identity_key()
71 .await
72 .map_err(|e| RemittanceError::Protocol(e.to_string()))?;
73
74 let server_msgs = self
75 .inner
76 .list_messages_lite(message_box, _host)
77 .await
78 .map_err(|e| RemittanceError::Protocol(e.to_string()))?;
79
80 Ok(server_msgs
81 .into_iter()
82 .map(|m| PeerMessage {
83 message_id: m.message_id,
84 sender: m.sender,
85 recipient: identity_key.clone(), message_box: message_box.to_string(), body: m.body,
88 })
89 .collect())
90 }
91
92 async fn acknowledge_message(
96 &self,
97 message_ids: &[String],
98 ) -> Result<(), RemittanceError> {
99 self.inner
100 .acknowledge_message(message_ids.to_vec(), None)
101 .await
102 .map_err(|e| RemittanceError::Protocol(e.to_string()))
103 }
104
105 async fn send_live_message(
116 &self,
117 recipient: &str,
118 message_box: &str,
119 body: &str,
120 host_override: Option<&str>,
121 ) -> Result<String, RemittanceError> {
122 self.inner
123 .send_live_message(recipient, message_box, body, false, false, None, host_override)
124 .await
125 .map(|d| d.message_id().to_string())
126 .map_err(|e| RemittanceError::Protocol(e.to_string()))
127 }
128
129 async fn listen_for_live_messages(
133 &self,
134 message_box: &str,
135 override_host: Option<&str>,
136 on_message: Arc<dyn Fn(PeerMessage) + Send + Sync>,
137 ) -> Result<(), RemittanceError> {
138 self.inner
139 .listen_for_live_messages(message_box, on_message, override_host)
140 .await
141 .map_err(|e| RemittanceError::Protocol(e.to_string()))
142 }
143}
144
145#[cfg(test)]
150mod tests {
151 use super::*;
152 use crate::types::ServerPeerMessage;
153 use bsv::primitives::private_key::PrivateKey;
154 use bsv::wallet::error::WalletError;
155 use bsv::wallet::interfaces::*;
156 use bsv::wallet::proto_wallet::ProtoWallet;
157
158 #[derive(Clone)]
160 struct ArcWallet(Arc<ProtoWallet>);
161
162 impl ArcWallet {
163 fn new() -> Self {
164 let key = PrivateKey::from_random().expect("random key");
165 ArcWallet(Arc::new(ProtoWallet::new(key)))
166 }
167 }
168
169 #[async_trait::async_trait]
170 impl WalletInterface for ArcWallet {
171 async fn create_action(&self, args: CreateActionArgs, orig: Option<&str>) -> Result<CreateActionResult, WalletError> { self.0.create_action(args, orig).await }
172 async fn sign_action(&self, args: SignActionArgs, orig: Option<&str>) -> Result<SignActionResult, WalletError> { self.0.sign_action(args, orig).await }
173 async fn abort_action(&self, args: AbortActionArgs, orig: Option<&str>) -> Result<AbortActionResult, WalletError> { self.0.abort_action(args, orig).await }
174 async fn list_actions(&self, args: ListActionsArgs, orig: Option<&str>) -> Result<ListActionsResult, WalletError> { self.0.list_actions(args, orig).await }
175 async fn internalize_action(&self, args: InternalizeActionArgs, orig: Option<&str>) -> Result<InternalizeActionResult, WalletError> { self.0.internalize_action(args, orig).await }
176 async fn list_outputs(&self, args: ListOutputsArgs, orig: Option<&str>) -> Result<ListOutputsResult, WalletError> { self.0.list_outputs(args, orig).await }
177 async fn relinquish_output(&self, args: RelinquishOutputArgs, orig: Option<&str>) -> Result<RelinquishOutputResult, WalletError> { self.0.relinquish_output(args, orig).await }
178 async fn get_public_key(&self, args: GetPublicKeyArgs, orig: Option<&str>) -> Result<GetPublicKeyResult, WalletError> { self.0.get_public_key(args, orig).await }
179 async fn reveal_counterparty_key_linkage(&self, args: RevealCounterpartyKeyLinkageArgs, orig: Option<&str>) -> Result<RevealCounterpartyKeyLinkageResult, WalletError> { self.0.reveal_counterparty_key_linkage(args, orig).await }
180 async fn reveal_specific_key_linkage(&self, args: RevealSpecificKeyLinkageArgs, orig: Option<&str>) -> Result<RevealSpecificKeyLinkageResult, WalletError> { self.0.reveal_specific_key_linkage(args, orig).await }
181 async fn encrypt(&self, args: EncryptArgs, orig: Option<&str>) -> Result<EncryptResult, WalletError> { self.0.encrypt(args, orig).await }
182 async fn decrypt(&self, args: DecryptArgs, orig: Option<&str>) -> Result<DecryptResult, WalletError> { self.0.decrypt(args, orig).await }
183 async fn create_hmac(&self, args: CreateHmacArgs, orig: Option<&str>) -> Result<CreateHmacResult, WalletError> { self.0.create_hmac(args, orig).await }
184 async fn verify_hmac(&self, args: VerifyHmacArgs, orig: Option<&str>) -> Result<VerifyHmacResult, WalletError> { self.0.verify_hmac(args, orig).await }
185 async fn create_signature(&self, args: CreateSignatureArgs, orig: Option<&str>) -> Result<CreateSignatureResult, WalletError> { self.0.create_signature(args, orig).await }
186 async fn verify_signature(&self, args: VerifySignatureArgs, orig: Option<&str>) -> Result<VerifySignatureResult, WalletError> { self.0.verify_signature(args, orig).await }
187 async fn acquire_certificate(&self, args: AcquireCertificateArgs, orig: Option<&str>) -> Result<Certificate, WalletError> { self.0.acquire_certificate(args, orig).await }
188 async fn list_certificates(&self, args: ListCertificatesArgs, orig: Option<&str>) -> Result<ListCertificatesResult, WalletError> { self.0.list_certificates(args, orig).await }
189 async fn prove_certificate(&self, args: ProveCertificateArgs, orig: Option<&str>) -> Result<ProveCertificateResult, WalletError> { self.0.prove_certificate(args, orig).await }
190 async fn relinquish_certificate(&self, args: RelinquishCertificateArgs, orig: Option<&str>) -> Result<RelinquishCertificateResult, WalletError> { self.0.relinquish_certificate(args, orig).await }
191 async fn discover_by_identity_key(&self, args: DiscoverByIdentityKeyArgs, orig: Option<&str>) -> Result<DiscoverCertificatesResult, WalletError> { self.0.discover_by_identity_key(args, orig).await }
192 async fn discover_by_attributes(&self, args: DiscoverByAttributesArgs, orig: Option<&str>) -> Result<DiscoverCertificatesResult, WalletError> { self.0.discover_by_attributes(args, orig).await }
193 async fn is_authenticated(&self, orig: Option<&str>) -> Result<AuthenticatedResult, WalletError> { self.0.is_authenticated(orig).await }
194 async fn wait_for_authentication(&self, orig: Option<&str>) -> Result<AuthenticatedResult, WalletError> { self.0.wait_for_authentication(orig).await }
195 async fn get_height(&self, orig: Option<&str>) -> Result<GetHeightResult, WalletError> { self.0.get_height(orig).await }
196 async fn get_header_for_height(&self, args: GetHeaderArgs, orig: Option<&str>) -> Result<GetHeaderResult, WalletError> { self.0.get_header_for_height(args, orig).await }
197 async fn get_network(&self, orig: Option<&str>) -> Result<GetNetworkResult, WalletError> { self.0.get_network(orig).await }
198 async fn get_version(&self, orig: Option<&str>) -> Result<GetVersionResult, WalletError> { self.0.get_version(orig).await }
199 }
200
201 fn make_client() -> Arc<MessageBoxClient<ArcWallet>> {
202 Arc::new(MessageBoxClient::new(
203 "https://example.com".to_string(),
204 ArcWallet::new(),
205 None,
206 bsv::services::overlay_tools::Network::Mainnet,
207 ))
208 }
209
210 #[test]
212 fn adapter_can_be_constructed() {
213 let client = make_client();
214 let _adapter = RemittanceAdapter::new(client);
215 }
216
217 #[test]
222 fn adapter_is_comms_layer() {
223 let client = make_client();
224 let adapter = Arc::new(RemittanceAdapter::new(client));
225 let _: Arc<dyn CommsLayer + Send + Sync> = adapter;
226 }
227
228 #[test]
233 fn map_server_message_all_five_fields() {
234 let server_msg = ServerPeerMessage {
235 message_id: "msg-001".to_string(),
236 body: "hello body".to_string(),
237 sender: "03senderkey".to_string(),
238 created_at: "2024-01-01T00:00:00Z".to_string(),
239 updated_at: "2024-01-01T00:01:00Z".to_string(),
240 acknowledged: None,
241 };
242 let identity_key = "03myidentitykey".to_string();
243 let message_box = "payment_inbox";
244
245 let peer_msg = PeerMessage {
247 message_id: server_msg.message_id.clone(),
248 sender: server_msg.sender.clone(),
249 recipient: identity_key.clone(),
250 message_box: message_box.to_string(),
251 body: server_msg.body.clone(),
252 };
253
254 assert_eq!(peer_msg.message_id, "msg-001");
255 assert_eq!(peer_msg.sender, "03senderkey");
256 assert_eq!(peer_msg.recipient, "03myidentitykey", "recipient from identity key");
257 assert_eq!(peer_msg.message_box, "payment_inbox", "message_box from parameter");
258 assert_eq!(peer_msg.body, "hello body");
259 }
260
261 #[tokio::test]
266 async fn recipient_from_identity_key() {
267 let client = make_client();
268 let identity_key = client
269 .get_identity_key()
270 .await
271 .expect("get_identity_key");
272
273 assert!(!identity_key.is_empty(), "identity key must not be empty");
274
275 let peer_msg = PeerMessage {
277 message_id: "x".to_string(),
278 sender: "03other".to_string(),
279 recipient: identity_key.clone(),
280 message_box: "inbox".to_string(),
281 body: "body".to_string(),
282 };
283
284 assert_eq!(peer_msg.recipient, identity_key);
285 assert_ne!(peer_msg.recipient, "", "recipient must not be empty string");
286 }
287
288 #[test]
292 fn acknowledge_message_accepts_slice() {
293 let ids: &[String] = &["id1".to_string(), "id2".to_string()];
297 let converted: Vec<String> = ids.to_vec();
298 assert_eq!(converted, vec!["id1", "id2"]);
299 }
300
301 #[allow(dead_code)]
306 fn send_live_message_compiles(adapter: &RemittanceAdapter<ArcWallet>) {
307 let _fut = adapter.send_live_message("03abc", "inbox", "hello", None);
308 }
309
310 #[allow(dead_code)]
314 fn listen_for_live_messages_compiles(adapter: &RemittanceAdapter<ArcWallet>) {
315 let cb: Arc<dyn Fn(PeerMessage) + Send + Sync> = Arc::new(|_msg| {});
316 let _fut = adapter.listen_for_live_messages("inbox", None, cb);
317 }
318
319 #[allow(dead_code)]
323 fn test_adapter_send_message_with_host_override_compiles(adapter: &RemittanceAdapter<ArcWallet>) {
324 let _fut = adapter.send_message("03recipient", "inbox", "body", Some("https://other.host"));
325 }
326
327 #[allow(dead_code)]
329 fn test_adapter_send_message_without_override_compiles(adapter: &RemittanceAdapter<ArcWallet>) {
330 let _fut = adapter.send_message("03recipient", "inbox", "body", None);
331 }
332}