Skip to content

Commit c324edd

Browse files
committed
add scan-all days command
1 parent c91f9dd commit c324edd

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,25 @@ Skip blockchain scanning (faster, but won't retrieve new messages):
135135
tari-advent show <day> --no-scan
136136
```
137137

138+
### Scan All Unlocked Doors
139+
140+
Scan all unlocked doors and display their messages in one command:
141+
142+
```bash
143+
tari-advent scan-all-open
144+
```
145+
146+
This command will:
147+
- Find all previously unlocked days
148+
- Scan the blockchain for each wallet
149+
- Display the wallet address and all messages for each day
150+
151+
With custom minotari executable path:
152+
153+
```bash
154+
tari-advent scan-all-open --executable /path/to/minotari
155+
```
156+
138157
### Generate Addresses (Admin)
139158

140159
Generate 24 encrypted wallet addresses from a CSV of passwords:

src/main.rs

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ enum Commands {
8383
#[arg(long)]
8484
no_scan: bool,
8585
},
86+
/// Scan all unlocked doors and display messages
87+
#[command(alias = "scan-all")]
88+
ScanAllOpen {
89+
/// Path to minotari executable (default: minotari.exe)
90+
#[arg(short, long, default_value = "minotari.exe")]
91+
executable: String,
92+
},
8693
}
8794

8895
#[tokio::main]
@@ -111,6 +118,10 @@ async fn main() -> Result<(), anyhow::Error> {
111118
let client = wallet_client::BinaryWalletClient::new(executable.clone());
112119
show_day(day, client, no_scan).await
113120
}
121+
Commands::ScanAllOpen { executable } => {
122+
let client = wallet_client::BinaryWalletClient::new(executable.clone());
123+
scan_all_open(client).await
124+
}
114125
}
115126
}
116127

@@ -634,6 +645,148 @@ async fn show_day<T: WalletClient>(day: Option<u8>, client: T, no_scan: bool) ->
634645
Ok(())
635646
}
636647

648+
async fn scan_all_open<T: WalletClient>(client: T) -> Result<(), anyhow::Error> {
649+
println!("🔍 Scanning all unlocked doors...\n");
650+
651+
let unlocked = load_unlocked_days();
652+
if unlocked.is_empty() {
653+
println!("No unlocked doors found. Use 'tari-advent open' to unlock a door first.");
654+
return Ok(());
655+
}
656+
657+
// Get all unlocked days sorted
658+
let mut unlocked_days: Vec<u8> = unlocked
659+
.keys()
660+
.filter_map(|key| {
661+
key.strip_prefix("day")
662+
.and_then(|day_str| day_str.parse::<u8>().ok())
663+
})
664+
.collect();
665+
unlocked_days.sort();
666+
667+
println!("Found {} unlocked door(s): {:?}\n", unlocked_days.len(), unlocked_days);
668+
669+
// Get encrypted data
670+
let encrypted_entries: Vec<&str> = ENCRYPTED_DAYS_CSV.lines().skip(1).collect();
671+
672+
// Get wallet directory
673+
let wallet_dir = get_wallet_dir()?;
674+
675+
for &day in &unlocked_days {
676+
println!("═══════════════════════════════════════════════════════════════");
677+
println!("📅 Day {}", day);
678+
println!("═══════════════════════════════════════════════════════════════");
679+
680+
// Get the saved password
681+
let password = match unlocked.get(&format!("day{}", day)) {
682+
Some(p) => p,
683+
None => {
684+
eprintln!("⚠️ Could not find password for day {}, skipping...\n", day);
685+
continue;
686+
}
687+
};
688+
689+
// Get encrypted data and decrypt to get keys
690+
let encrypted_data = match encrypted_entries.get(day as usize - 1) {
691+
Some(data) => data,
692+
None => {
693+
eprintln!("⚠️ No encrypted data found for day {}, skipping...\n", day);
694+
continue;
695+
}
696+
};
697+
698+
let (view_key, spend_key) = match decrypt_keys(encrypted_data, password) {
699+
Ok(keys) => keys,
700+
Err(e) => {
701+
eprintln!("⚠️ Failed to decrypt keys for day {}: {}, skipping...\n", day, e);
702+
continue;
703+
}
704+
};
705+
706+
let wallet_file = wallet_dir.join(format!("wallet-day-{}.sqlite", day));
707+
708+
// Only import if the database doesn't exist
709+
if !wallet_file.exists() {
710+
println!("📂 Importing keys for day {}...", day);
711+
if let Err(e) = client.import_view_key(&view_key, &spend_key, password, &wallet_file).await {
712+
eprintln!("⚠️ Failed to import keys for day {}: {}, skipping...\n", day, e);
713+
continue;
714+
}
715+
}
716+
717+
println!("🔍 Scanning wallet for day {}...", day);
718+
if let Err(e) = client.scan(&wallet_file, "password1", "https://rpc.tari.com").await {
719+
eprintln!("⚠️ Scan failed for day {}: {}, continuing...\n", day, e);
720+
}
721+
722+
// Construct TariAddress from the keys
723+
match construct_tari_address(&view_key, &spend_key) {
724+
Ok(address) => {
725+
let display_address = address.to_base58();
726+
println!("🏦 Wallet Address: {}", display_address);
727+
}
728+
Err(e) => {
729+
eprintln!("⚠️ Warning: Could not construct Tari address: {}", e);
730+
}
731+
}
732+
733+
// Query the outputs table for messages
734+
println!("\n📬 Messages:");
735+
match Connection::open(&wallet_file) {
736+
Ok(conn) => {
737+
match conn.prepare(
738+
"SELECT mined_timestamp, memo_parsed FROM outputs WHERE memo_parsed IS NOT NULL AND memo_parsed != '' ORDER BY mined_timestamp"
739+
) {
740+
Ok(mut stmt) => {
741+
match stmt.query_map([], |row| {
742+
Ok((
743+
row.get::<_, String>(0)?,
744+
row.get::<_, String>(1)?,
745+
))
746+
}) {
747+
Ok(messages) => {
748+
let mut found_any = false;
749+
for message in messages {
750+
match message {
751+
Ok((timestamp, memo)) => {
752+
found_any = true;
753+
println!(" [{}] {}", timestamp, memo);
754+
}
755+
Err(e) => {
756+
eprintln!("⚠️ Error reading message: {}", e);
757+
}
758+
}
759+
}
760+
if !found_any {
761+
println!(" (No messages found)");
762+
}
763+
}
764+
Err(e) => {
765+
eprintln!("⚠️ Could not query messages: {}", e);
766+
println!(" (No messages found or database error)");
767+
}
768+
}
769+
}
770+
Err(e) => {
771+
eprintln!("⚠️ Could not prepare query: {}", e);
772+
println!(" (No messages found or database error)");
773+
}
774+
}
775+
conn.close().ok();
776+
}
777+
Err(e) => {
778+
eprintln!("⚠️ Could not open database: {}", e);
779+
println!(" (No messages found or database error)");
780+
}
781+
}
782+
783+
println!();
784+
}
785+
786+
println!("✅ Scan complete! Scanned {} door(s).", unlocked_days.len());
787+
Ok(())
788+
}
789+
637790
fn construct_tari_address(
638791
view_key_hex: &str,
639792
spend_key_hex: &str,

0 commit comments

Comments
 (0)