Repository: farcaster-project/farcaster-node
Pull request #316 (2021-12-07 15:52:53)

Syncer: Add GetTransaction endpoint


Patch:
diff --git a/src/swapd/runtime.rs b/src/swapd/runtime.rs
index e4d0c648..edc481d1 100644
--- a/src/swapd/runtime.rs
+++ b/src/swapd/runtime.rs
@@ -1871,6 +1871,9 @@ impl Runtime {
                     Event::SweepSuccess(event) => {
                         debug!("{}", event)
                     }
+                    Event::TransactionRetrieved(event) => {
+                        debug!("{}", event)
+                    }
                 }
             }
             Request::Protocol(Msg::CoreArbitratingSetup(core_arb_setup)) if self.state.reveal() => {
diff --git a/src/syncerd/bitcoin_syncer.rs b/src/syncerd/bitcoin_syncer.rs
index 02788e04..944ab374 100644
--- a/src/syncerd/bitcoin_syncer.rs
+++ b/src/syncerd/bitcoin_syncer.rs
@@ -13,6 +13,7 @@ use crate::syncerd::BtcAddressAddendum;
 use crate::syncerd::Event;
 use crate::syncerd::TaskTarget;
 use crate::syncerd::TransactionBroadcasted;
+use crate::syncerd::TransactionRetrieved;
 use crate::ServiceId;
 use crate::{error::Error, syncerd::syncer_state::create_set};
 use crate::{error::SyncerError, internet2::Duplex};
@@ -447,6 +448,47 @@ async fn run_syncerd_task_receiver(
             match syncerd_task {
                 Ok(syncerd_task) => {
                     match syncerd_task.task {
+                        Task::GetTx(task) => {
+                            match Client::new(&electrum_server).and_then(|transaction_client| {
+                                transaction_client.transaction_get(
+                                    &bitcoin::Txid::from_slice(&task.hash).unwrap(),
+                                )
+                            }) {
+                                Ok(tx) => {
+                                    tx_event
+                                        .send(SyncerdBridgeEvent {
+                                            event: Event::TransactionRetrieved(
+                                                TransactionRetrieved {
+                                                    id: task.id,
+                                                    tx: Some(tx),
+                                                },
+                                            ),
+                                            source: syncerd_task.source,
+                                        })
+                                        .await
+                                        .expect("error sending transaction retrieved event");
+                                    info!(
+                                        "successfully retrieved tx: {:?}",
+                                        hex::encode(task.hash)
+                                    );
+                                }
+                                Err(e) => {
+                                    tx_event
+                                        .send(SyncerdBridgeEvent {
+                                            event: Event::TransactionRetrieved(
+                                                TransactionRetrieved {
+                                                    id: task.id,
+                                                    tx: None,
+                                                },
+                                            ),
+                                            source: syncerd_task.source,
+                                        })
+                                        .await
+                                        .expect("error sending transaction retrieved event");
+                                    info!("failed to retrieved tx: {:?}", e);
+                                }
+                            }
+                        }
                         Task::SweepAddress(_) => {
                             error!("sweep address not implemented for bitcoin syncer");
                         }
diff --git a/src/syncerd/monero_syncer.rs b/src/syncerd/monero_syncer.rs
index 7f10a29b..80dc7e32 100644
--- a/src/syncerd/monero_syncer.rs
+++ b/src/syncerd/monero_syncer.rs
@@ -338,6 +338,9 @@ async fn run_syncerd_task_receiver(
             match syncerd_task {
                 Ok(syncerd_task) => {
                     match syncerd_task.task {
+                        Task::GetTx(_) => {
+                            error!("get tx not implemented for monero syncer");
+                        }
                         Task::SweepAddress(task) => match task.addendum.clone() {
                             SweepAddressAddendum::Monero(sweep) => {
                                 let addr = sweep.address;
diff --git a/src/syncerd/types.rs b/src/syncerd/types.rs
index ec339759..c3f9ec9d 100644
--- a/src/syncerd/types.rs
+++ b/src/syncerd/types.rs
@@ -197,6 +197,13 @@ pub struct BroadcastTransaction {
     pub tx: Vec<u8>,
 }
 
+#[derive(Clone, Debug, Display, StrictEncode, StrictDecode, Eq, PartialEq, Hash)]
+#[display(Debug)]
+pub struct GetTx {
+    pub id: TaskId,
+    pub hash: Vec<u8>,
+}
+
 /// Tasks created by the daemon and handle by syncers to process a blockchain
 /// and generate [`Event`] back to the syncer.
 #[derive(Clone, Debug, Display, StrictEncode, StrictDecode, Eq, PartialEq, Hash)]
@@ -208,6 +215,7 @@ pub enum Task {
     WatchTransaction(WatchTransaction),
     BroadcastTransaction(BroadcastTransaction),
     SweepAddress(SweepAddress),
+    GetTx(GetTx),
 }
 
 #[derive(Clone, Debug, Display, StrictEncode, StrictDecode, Eq, PartialEq, Hash)]
@@ -261,6 +269,14 @@ pub struct SweepSuccess {
     pub txids: Vec<Vec<u8>>,
 }
 
+#[derive(Clone, Debug, Display, StrictEncode, StrictDecode, Eq, PartialEq, Hash)]
+#[display(Debug)]
+pub struct TransactionRetrieved {
+    pub id: TaskId,
+    // for bitcoin with bitcoin::consensus encoding
+    pub tx: Option<bitcoin::Transaction>,
+}
+
 /// Events returned by syncers to the daemon to update the blockchain states.
 /// Events are identified with a unique 32-bits integer that match the [`Task`]
 /// id.
@@ -276,4 +292,5 @@ pub enum Event {
     /// Notify the daemon the task has been aborted with success or failure.
     /// Carries the status for the task abortion.
     TaskAborted(TaskAborted),
+    TransactionRetrieved(TransactionRetrieved),
 }


Test patch:
diff --git a/tests/functional.rs b/tests/functional.rs
index c5a5bdc5..e051f4f1 100644
--- a/tests/functional.rs
+++ b/tests/functional.rs
@@ -33,7 +33,7 @@ use std::str::FromStr;
 
 use farcaster_core::blockchain::Network;
 use farcaster_node::syncerd::types::{
-    Abort, AddressAddendum, Boolean, BroadcastTransaction, BtcAddressAddendum, Event, Task,
+    Abort, AddressAddendum, Boolean, BroadcastTransaction, BtcAddressAddendum, Event, GetTx, Task,
     WatchAddress, WatchHeight, WatchTransaction,
 };
 
@@ -74,6 +74,60 @@ make_polling_test!(bitcoin_syncer_address_test);
 make_polling_test!(bitcoin_syncer_transaction_test);
 make_polling_test!(bitcoin_syncer_broadcast_tx_test);
 
+#[test]
+#[timeout(300000)]
+#[ignore]
+fn bicoin_syncer_retrieve_transaction_test() {
+    let bitcoin_rpc = bitcoin_setup();
+    let address = bitcoin_rpc.get_new_address(None, None).unwrap();
+
+    let (tx, rx_event) = create_bitcoin_syncer(true, "transaction");
+
+    let txid = bitcoin_rpc
+        .send_to_address(
+            &address,
+            bitcoin::Amount::from_sat(500),
+            None,
+            None,
+            None,
+            None,
+            None,
+            None,
+        )
+        .unwrap();
+
+    let duration = std::time::Duration::from_secs(30);
+    std::thread::sleep(duration);
+
+    let task = SyncerdTask {
+        task: Task::GetTx(GetTx {
+            id: TaskId(1),
+            hash: txid.to_vec(),
+        }),
+        source: SOURCE1.clone(),
+    };
+    tx.send(task).unwrap();
+    let message = rx_event.recv_multipart(0).unwrap();
+    let request = get_request_from_message(message);
+    println!("received request: {:?}", request);
+}
+
+fn assert_transaction_received(request: Request, expected_txid: bitcoin::Txid) {
+    match request {
+        Request::SyncerdBridgeEvent(event) => match event.event {
+            Event::TransactionRetrieved(transaction) => {
+                assert_eq!(transaction.tx.unwrap().txid(), expected_txid);
+            }
+            _ => {
+                panic!("expected height changed event");
+            }
+        },
+        _ => {
+            panic!("expected syncerd bridge event");
+        }
+    }
+}
+
 /*
 We test for the following scenarios in the block height tests: