use amp_rs::ElementsRpc;
use dotenvy;
use std::env;
use std::process::Command;
const WALLET_NAME: &str = "amp_elements_wallet_static_for_funding";
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
println!("๐ Automated Wallet Migration with Signing Support");
println!("==================================================");
println!("Wallet: {}", WALLET_NAME);
println!();
dotenvy::dotenv().ok();
let local_url =
env::var("ELEMENTS_RPC_URL").map_err(|_| "ELEMENTS_RPC_URL not set in environment")?;
let local_user =
env::var("ELEMENTS_RPC_USER").map_err(|_| "ELEMENTS_RPC_USER not set in environment")?;
let local_password = env::var("ELEMENTS_RPC_PASSWORD")
.map_err(|_| "ELEMENTS_RPC_PASSWORD not set in environment")?;
let cloud_url = env::var("CLOUD_ELEMENTS_RPC_URL")
.map_err(|_| "CLOUD_ELEMENTS_RPC_URL not set in environment")?;
let cloud_user = env::var("CLOUD_ELEMENTS_RPC_USER")
.map_err(|_| "CLOUD_ELEMENTS_RPC_USER not set in environment")?;
let cloud_password = env::var("CLOUD_ELEMENTS_RPC_PASSWORD")
.map_err(|_| "CLOUD_ELEMENTS_RPC_PASSWORD not set in environment")?;
let cloud_ip = cloud_url
.split("://")
.nth(1)
.and_then(|s| s.split(':').next())
.ok_or("Could not extract IP from CLOUD_ELEMENTS_RPC_URL")?;
println!("๐ก Connection Details:");
println!(" Local: {}", local_url);
println!(" Cloud: {} ({})", cloud_url, cloud_ip);
println!();
let local_rpc = ElementsRpc::new(local_url.clone(), local_user, local_password);
let cloud_rpc = ElementsRpc::new(cloud_url.clone(), cloud_user, cloud_password);
println!("๐ Testing Connectivity...");
local_rpc.get_network_info().await?;
println!("โ
Connected to local node");
cloud_rpc.get_network_info().await?;
println!("โ
Connected to cloud node");
println!();
println!("๐ Step 1: Exporting wallet from local node...");
match local_rpc.load_wallet(WALLET_NAME).await {
Ok(()) => println!("โ
Wallet loaded"),
Err(e) if e.to_string().contains("already loaded") => println!("โ
Wallet already loaded"),
Err(e) => return Err(e.into()),
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs();
let export_filename = format!("{}_export_{}.dat", WALLET_NAME, timestamp);
let export_path = format!("/tmp/{}", export_filename);
local_rpc.dump_wallet(WALLET_NAME, &export_path).await?;
println!("โ
Wallet exported to: {}", export_path);
println!();
println!("๐ค Step 2: Unloading cloud wallet...");
match cloud_rpc.unload_wallet(WALLET_NAME).await {
Ok(()) => println!("โ
Wallet unloaded"),
Err(e) if e.to_string().contains("not found") => println!("โน๏ธ Wallet not loaded"),
Err(e) => println!("โ ๏ธ Unload error (continuing anyway): {}", e),
}
println!();
println!("๐๏ธ Step 3: Deleting old wallet directory on cloud server...");
let ssh_delete = Command::new("ssh")
.arg(format!("ubuntu@{}", cloud_ip))
.arg(format!(
"sudo docker exec elements-testnet rm -rf /root/.elements/liquidtestnet/wallets/{}",
WALLET_NAME
))
.output();
match ssh_delete {
Ok(output) => {
if output.status.success() {
println!("โ
Wallet directory deleted on cloud");
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
println!("โ ๏ธ SSH delete warning: {}", stderr);
println!(" Continuing anyway...");
}
}
Err(e) => {
println!("โ ๏ธ SSH delete failed: {}", e);
println!(
" You may need to manually delete: ~/.elements/liquidv1/wallets/{}",
WALLET_NAME
);
println!(" Or the wallet may not exist yet, which is fine.");
}
}
println!();
println!("๐ฆ Step 4: Copying wallet file to cloud server...");
let scp_output = Command::new("scp")
.arg(&export_path)
.arg(format!("ubuntu@{}:/tmp/{}", cloud_ip, export_filename))
.output();
match scp_output {
Ok(output) => {
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("SCP to host failed: {}", stderr).into());
}
}
Err(e) => {
return Err(format!("Failed to execute SCP: {}", e).into());
}
}
println!("โ
Wallet file copied to host");
let docker_cp = Command::new("ssh")
.arg(format!("ubuntu@{}", cloud_ip))
.arg(format!(
"sudo docker cp /tmp/{} elements-testnet:/tmp/{}",
export_filename, export_filename
))
.output();
match docker_cp {
Ok(output) => {
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("Docker cp failed: {}", stderr).into());
}
}
Err(e) => {
return Err(format!("Failed to copy into Docker: {}", e).into());
}
}
println!("โ
Wallet file copied into Docker container");
println!();
println!("๐๏ธ Step 5: Creating fresh wallet on cloud node...");
match cloud_rpc.create_elements_wallet(WALLET_NAME).await {
Ok(()) => println!("โ
Wallet created"),
Err(e) => {
println!("โ Failed to create wallet: {}", e);
println!(" Make sure the wallet directory was deleted!");
return Err(e.into());
}
}
println!();
println!("๐ฅ Step 6: Importing wallet with private keys...");
let cloud_import_path = format!("/tmp/{}", export_filename);
cloud_rpc
.import_wallet(WALLET_NAME, &cloud_import_path)
.await?;
println!("โ
Wallet imported successfully");
println!(" All private keys and blinding keys are now on cloud node");
println!();
println!("๐งน Step 7: Cleaning up temporary files...");
match std::fs::remove_file(&export_path) {
Ok(()) => println!("โ
Removed local export file"),
Err(e) => println!("โ ๏ธ Could not remove local file: {}", e),
}
let ssh_cleanup = Command::new("ssh")
.arg(format!("ubuntu@{}", cloud_ip))
.arg(format!(
"sudo docker exec elements-testnet rm -f {}",
cloud_import_path
))
.output();
match ssh_cleanup {
Ok(output) => {
if output.status.success() {
println!("โ
Removed cloud export file");
} else {
println!("โ ๏ธ Could not remove cloud file (not critical)");
}
}
Err(_) => {
println!("โ ๏ธ Could not remove cloud file (not critical)");
}
}
println!();
println!("โ๏ธ Step 8: Verifying signing capability...");
match cloud_rpc.get_new_address(WALLET_NAME, None).await {
Ok(address) => match cloud_rpc.dump_private_key(WALLET_NAME, &address).await {
Ok(_) => {
println!("โ
SUCCESS! Cloud wallet can sign transactions");
}
Err(e) => {
println!("โ FAILED: Cloud wallet cannot access private keys");
println!(" Error: {}", e);
return Err(e.into());
}
},
Err(e) => {
println!("โ Failed to generate test address: {}", e);
return Err(e.into());
}
}
println!();
println!("๐ Migration Complete!");
println!("======================");
println!(
"โ
Cloud wallet '{}' can now sign transactions",
WALLET_NAME
);
println!();
println!("๐ก Next steps:");
println!(" 1. Rescan blockchain on cloud node:");
println!(" ssh ubuntu@{} 'sudo docker exec elements-testnet elements-cli -rpcwallet={} rescanblockchain'", cloud_ip, WALLET_NAME);
println!();
println!(" 2. Test sending a transaction from the cloud wallet");
Ok(())
}