Repository: ordinals/ord (rust)

Issue:
Trigger wallet rescan on wallet import/restore
One user [reported](https://gist.github.com/windsok/5b53a1ced6ef3eddbde260337de28980?permalink_comment_id=4463824#gistcomment-4463824) that after importing a wallet they originally created with Sparrow, `ord` was not showing any of the inscriptions they had received to the wallet. To fix the issue they initiated a manual rescan with `rescanblockchain` using `bitcoin-cli`.

Not sure if they did something weird when restoring the wallet, or if we need to add some logic to automatically trigger a rescan in bitcoin core when wallets are imported.

Pull request description:
Rescan wallet when restoring from mnemonic 
Finally fixes #1589.

Patch:
diff --git a/crates/mockcore/src/lib.rs b/crates/mockcore/src/lib.rs
index 63a191d6..3d0510ff 100644
--- a/crates/mockcore/src/lib.rs
+++ b/crates/mockcore/src/lib.rs
@@ -321,11 +321,16 @@ impl Handle {
   }
 
   pub fn descriptors(&self) -> Vec<String> {
-    self.state().descriptors.clone()
+    self
+      .state()
+      .descriptors
+      .iter()
+      .map(|(descriptor, _timestamp)| descriptor.clone())
+      .collect()
   }
 
   pub fn import_descriptor(&self, desc: String) {
-    self.state().descriptors.push(desc);
+    self.state().descriptors.push((desc, Timestamp::Now));
   }
 
   pub fn lock(&self, output: OutPoint) {
diff --git a/crates/mockcore/src/server.rs b/crates/mockcore/src/server.rs
index aa05e64d..a79a16cc 100644
--- a/crates/mockcore/src/server.rs
+++ b/crates/mockcore/src/server.rs
@@ -803,10 +803,11 @@ impl Api for Server {
     &self,
     req: Vec<ImportDescriptors>,
   ) -> Result<Vec<ImportMultiResult>, jsonrpc_core::Error> {
-    self
-      .state()
-      .descriptors
-      .extend(req.into_iter().map(|params| params.descriptor));
+    self.state().descriptors.extend(
+      req
+        .into_iter()
+        .map(|params| (params.descriptor, params.timestamp)),
+    );
 
     Ok(vec![ImportMultiResult {
       success: true,
@@ -901,9 +902,9 @@ impl Api for Server {
         .state()
         .descriptors
         .iter()
-        .map(|desc| Descriptor {
+        .map(|(desc, timestamp)| Descriptor {
           desc: desc.to_string(),
-          timestamp: Timestamp::Now,
+          timestamp: *timestamp,
           active: true,
           internal: None,
           range: None,
diff --git a/crates/mockcore/src/state.rs b/crates/mockcore/src/state.rs
index 40e6ee55..b036c250 100644
--- a/crates/mockcore/src/state.rs
+++ b/crates/mockcore/src/state.rs
@@ -10,7 +10,7 @@ use {
 #[derive(Debug)]
 pub struct State {
   pub blocks: BTreeMap<BlockHash, Block>,
-  pub descriptors: Vec<String>,
+  pub descriptors: Vec<(String, bitcoincore_rpc::json::Timestamp)>,
   pub fail_lock_unspent: bool,
   pub hashes: Vec<BlockHash>,
   pub loaded_wallets: BTreeSet<String>,
diff --git a/src/subcommand/wallet/create.rs b/src/subcommand/wallet/create.rs
index 768071b0..136b8b50 100644
--- a/src/subcommand/wallet/create.rs
+++ b/src/subcommand/wallet/create.rs
@@ -26,7 +26,12 @@ impl Create {
 
     let mnemonic = Mnemonic::from_entropy(&entropy)?;
 
-    Wallet::initialize(name, settings, mnemonic.to_seed(&self.passphrase))?;
+    Wallet::initialize(
+      name,
+      settings,
+      mnemonic.to_seed(&self.passphrase),
+      bitcoincore_rpc::json::Timestamp::Now,
+    )?;
 
     Ok(Some(Box::new(Output {
       mnemonic,
diff --git a/src/subcommand/wallet/restore.rs b/src/subcommand/wallet/restore.rs
index f4569043..18babb5c 100644
--- a/src/subcommand/wallet/restore.rs
+++ b/src/subcommand/wallet/restore.rs
@@ -45,6 +45,7 @@ impl Restore {
           name,
           settings,
           mnemonic.to_seed(self.passphrase.unwrap_or_default()),
+          bitcoincore_rpc::json::Timestamp::Time(0),
         )?;
       }
     }
diff --git a/src/wallet.rs b/src/wallet.rs
index 0ee430a5..f0221501 100644
--- a/src/wallet.rs
+++ b/src/wallet.rs
@@ -7,7 +7,7 @@ use {
     bip32::{ChildNumber, DerivationPath, Fingerprint, Xpriv},
     psbt::Psbt,
   },
-  bitcoincore_rpc::bitcoincore_rpc_json::{ImportDescriptors, Timestamp},
+  bitcoincore_rpc::json::ImportDescriptors,
   entry::{EtchingEntry, EtchingEntryValue},
   fee_rate::FeeRate,
   index::entry::Entry,
@@ -495,7 +495,12 @@ impl Wallet {
     Ok(())
   }
 
-  pub(crate) fn initialize(name: String, settings: &Settings, seed: [u8; 64]) -> Result {
+  pub(crate) fn initialize(
+    name: String,
+    settings: &Settings,
+    seed: [u8; 64],
+    timestamp: bitcoincore_rpc::json::Timestamp,
+  ) -> Result {
     Self::check_version(settings.bitcoin_rpc_client(None)?)?.create_wallet(
       &name,
       None,
@@ -529,6 +534,7 @@ impl Wallet {
         (fingerprint, derivation_path.clone()),
         derived_private_key,
         change,
+        timestamp,
       )?;
     }
 
@@ -542,6 +548,7 @@ impl Wallet {
     origin: (Fingerprint, DerivationPath),
     derived_private_key: Xpriv,
     change: bool,
+    timestamp: bitcoincore_rpc::json::Timestamp,
   ) -> Result {
     let secret_key = DescriptorSecretKey::XPrv(DescriptorXKey {
       origin: Some(origin),
@@ -563,7 +570,7 @@ impl Wallet {
       .bitcoin_rpc_client(Some(name.clone()))?
       .import_descriptors(ImportDescriptors {
         descriptor: descriptor.to_string_with_secret(&key_map),
-        timestamp: Timestamp::Now,
+        timestamp,
         active: Some(true),
         range: None,
         next_index: None,


Test patch:
diff --git a/tests/wallet/restore.rs b/tests/wallet/restore.rs
index 66b594e5..ace4eaea 100644
--- a/tests/wallet/restore.rs
+++ b/tests/wallet/restore.rs
@@ -5,10 +5,24 @@ fn restore_generates_same_descriptors() {
   let (mnemonic, descriptors) = {
     let core = mockcore::spawn();
 
+    let ord = TestServer::spawn(&core);
+
     let create::Output { mnemonic, .. } = CommandBuilder::new("wallet create")
       .core(&core)
       .run_and_deserialize_output();
 
+    let output = CommandBuilder::new("wallet dump")
+      .core(&core)
+      .ord(&ord)
+      .stderr_regex(".*THIS STRING CONTAINS YOUR PRIVATE KEYS.*")
+      .run_and_deserialize_output::<ListDescriptorsResult>();
+
+    // new descriptors are created with timestamp `now`
+    assert!(output
+      .descriptors
+      .iter()
+      .all(|descriptor| descriptor.timestamp == bitcoincore_rpc::json::Timestamp::Now));
+
     (mnemonic, core.descriptors())
   };
 
@@ -19,6 +33,20 @@ fn restore_generates_same_descriptors() {
     .core(&core)
     .run_and_extract_stdout();
 
+  let ord = TestServer::spawn(&core);
+
+  let output = CommandBuilder::new("wallet dump")
+    .core(&core)
+    .ord(&ord)
+    .stderr_regex(".*THIS STRING CONTAINS YOUR PRIVATE KEYS.*")
+    .run_and_deserialize_output::<ListDescriptorsResult>();
+
+  // restored descriptors are created with timestamp `0`
+  assert!(output
+    .descriptors
+    .iter()
+    .all(|descriptor| descriptor.timestamp == bitcoincore_rpc::json::Timestamp::Time(0)));
+
   assert_eq!(core.descriptors(), descriptors);
 }