110 lines
3.4 KiB
Rust
110 lines
3.4 KiB
Rust
use blockchain::common::binary_conversions::binary_to_string;
|
|
use blockchain::common::cli_prompts::prompt_hidden_nonempty;
|
|
use blockchain::common::network_startup::get_connections;
|
|
use blockchain::env;
|
|
use blockchain::json;
|
|
use blockchain::records::memory::response_channels::generate_uid;
|
|
use blockchain::standalone_tools::connections::handshake;
|
|
use blockchain::to_string_pretty;
|
|
|
|
fn decode_token_list(response: &[u8]) -> Option<String> {
|
|
// An empty binary payload means the peer has no token index entries.
|
|
if response.is_empty() {
|
|
return Some("{\n \"tokens\": []\n}".to_string());
|
|
}
|
|
|
|
// Each token-list row is 15 bytes token name and 64 bytes token hash text.
|
|
if response.len() % 79 != 0 {
|
|
return None;
|
|
}
|
|
|
|
let mut tokens = Vec::new();
|
|
let mut offset = 0;
|
|
while offset + 79 <= response.len() {
|
|
// Convert fixed-width padded fields back into display strings.
|
|
let token = binary_to_string(response[offset..offset + 15].to_vec())
|
|
.trim()
|
|
.to_string();
|
|
let hash = binary_to_string(response[offset + 15..offset + 79].to_vec())
|
|
.trim()
|
|
.to_string();
|
|
if !token.is_empty() && !hash.is_empty() {
|
|
tokens.push(json!({
|
|
"token": token,
|
|
"hash": hash,
|
|
}));
|
|
}
|
|
offset += 79;
|
|
}
|
|
|
|
let output = json!({
|
|
"tokens": tokens,
|
|
});
|
|
to_string_pretty(&output).ok()
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
// Command 31 asks a peer for the full token list.
|
|
let hashmap_key = generate_uid();
|
|
let rpc_command = 31;
|
|
|
|
// This lookup takes no user arguments beyond the wallet key used for handshake auth.
|
|
let args: Vec<String> = env::args().collect();
|
|
if args.len() != 1 {
|
|
println!("Usage: ./token_list");
|
|
return;
|
|
}
|
|
|
|
let encryption_key = prompt_hidden_nonempty(
|
|
"What is your wallet decryption key? ",
|
|
"Wallet key cannot be empty. Please try again.",
|
|
)
|
|
.await;
|
|
// Try each configured peer until one returns a parsable token list or text error.
|
|
let connections = get_connections().await;
|
|
let mut connected = false;
|
|
|
|
for conn in connections {
|
|
if connected {
|
|
break;
|
|
}
|
|
|
|
let socket_address = conn.parse().expect("Failed to parse the socket address");
|
|
let result = handshake::connect_and_handshake(
|
|
socket_address,
|
|
"".to_string(),
|
|
rpc_command,
|
|
handshake::HandshakeWallet::WalletKey(encryption_key.clone()),
|
|
hashmap_key,
|
|
)
|
|
.await;
|
|
|
|
match result {
|
|
Ok(response) => {
|
|
// Prefer binary token-list decoding; otherwise print a text error if one was returned.
|
|
if let Some(output) = decode_token_list(&response) {
|
|
println!("{output}");
|
|
connected = true;
|
|
} else {
|
|
let response_text = String::from_utf8_lossy(&response);
|
|
let trimmed = response_text.trim();
|
|
if !trimmed.is_empty() {
|
|
println!("{trimmed}");
|
|
connected = true;
|
|
} else {
|
|
connected = false;
|
|
}
|
|
}
|
|
}
|
|
Err(_) => {
|
|
connected = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if !connected {
|
|
eprintln!("failed to connect");
|
|
}
|
|
}
|