use amp_rs::ElementsRpc;
use dotenvy;
use std::env;
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!("๐ Wallet Migration: Local โ Cloud");
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")?;
println!("๐ก Connection Details:");
println!(" Local: {}", local_url);
println!(" Cloud: {}", cloud_url);
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...");
match local_rpc.get_network_info().await {
Ok(info) => {
println!("โ
Connected to local node (version: {})", info.version);
}
Err(e) => {
println!("โ Failed to connect to local node: {}", e);
return Err(e.into());
}
}
match cloud_rpc.get_network_info().await {
Ok(info) => {
println!("โ
Connected to cloud node (version: {})", info.version);
}
Err(e) => {
println!("โ Failed to connect to cloud node: {}", e);
return Err(e.into());
}
}
println!();
println!("๐ Step 1: Loading wallet on local node...");
match local_rpc.load_wallet(WALLET_NAME).await {
Ok(()) => {
println!("โ
Wallet loaded successfully");
}
Err(e) => {
let error_msg = e.to_string();
if error_msg.contains("already loaded") {
println!("โ
Wallet already loaded");
} else {
println!("โ Failed to load wallet: {}", e);
return Err(e.into());
}
}
}
println!();
println!("๐ Step 2: Checking wallet type...");
let wallet_info = local_rpc.get_wallet_info(WALLET_NAME).await?;
let is_descriptor_wallet = wallet_info
.get("descriptors")
.and_then(|v| v.as_bool())
.unwrap_or(false);
if is_descriptor_wallet {
println!("โ
Descriptor wallet detected");
println!(
" For descriptor wallets, use migrate_test_wallet_to_cloud_descriptors.rs instead"
);
println!(" That script will properly migrate the HD wallet structure and blinding keys.");
return Ok(());
} else {
println!("โ
Legacy wallet detected");
println!(
" Will use dumpwallet/importwallet for complete migration including blinding keys"
);
}
println!();
println!("๐พ Step 3: Exporting wallet from local node using dumpwallet...");
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);
match local_rpc.dump_wallet(WALLET_NAME, &export_path).await {
Ok(()) => {
println!("โ
Wallet exported to: {}", export_path);
}
Err(e) => {
println!("โ Failed to export wallet: {}", e);
println!(" Note: The export path must be writable by the Elements node");
return Err(e.into());
}
}
println!();
println!("๐ฆ Step 4: Preparing wallet file for cloud import...");
println!(" Local export file: {}", export_path);
println!();
let same_server = local_url.contains("127.0.0.1") || local_url.contains("localhost");
if same_server {
println!(" โ ๏ธ Local and cloud appear to be different servers");
println!(" The wallet file needs to be accessible to the cloud Elements node");
println!();
println!(" Manual steps required:");
println!(" 1. Copy the wallet file to the cloud server:");
println!(" scp {} user@cloud-server:/tmp/", export_path);
println!();
println!(" 2. Then run importwallet on the cloud node:");
println!(
" elements-cli -rpcwallet={} importwallet /tmp/{}",
WALLET_NAME, export_filename
);
println!();
println!(" 3. After import, rescan the blockchain:");
println!(
" elements-cli -rpcwallet={} rescanblockchain",
WALLET_NAME
);
println!();
println!("Migration export complete. Manual import steps required on cloud server.");
return Ok(());
} else {
println!(" โ
Nodes appear to share filesystem, proceeding with import...");
println!();
}
println!("๐๏ธ Step 5: Creating wallet on cloud node...");
match cloud_rpc.create_elements_wallet(WALLET_NAME).await {
Ok(()) => {
println!("โ
Wallet created on cloud node");
}
Err(e) => {
let error_msg = e.to_string();
if error_msg.contains("already exists") || error_msg.contains("Database already exists")
{
println!("โ ๏ธ Wallet already exists on cloud node");
println!(" The import will add keys to the existing wallet");
} else {
println!("โ Failed to create wallet on cloud node: {}", e);
return Err(e.into());
}
}
}
println!();
println!("๐ฅ Step 6: Importing wallet to cloud node...");
println!(" This will import all private keys and the master blinding key");
match cloud_rpc.import_wallet(WALLET_NAME, &export_path).await {
Ok(()) => {
println!("โ
Wallet imported successfully");
println!(" All keys (including blinding keys) are now available on cloud node");
}
Err(e) => {
println!("โ Failed to import wallet: {}", e);
println!();
println!(" Troubleshooting:");
println!(" 1. Ensure the wallet file exists at: {}", export_path);
println!(" 2. Ensure the cloud Elements node can read the file");
println!(" 3. Check file permissions");
println!(" 4. If nodes are on different servers, copy the file first:");
println!(" scp {} user@cloud-server:/tmp/", export_path);
return Err(e.into());
}
}
println!();
println!("๐งน Step 7: Cleaning up...");
match std::fs::remove_file(&export_path) {
Ok(()) => {
println!("โ
Removed temporary export file");
}
Err(e) => {
println!("โ ๏ธ Could not remove export file: {}", e);
println!(" You may want to manually delete: {}", export_path);
}
}
println!();
println!("โ๏ธ Step 8: Verifying migration...");
let local_info = local_rpc.get_wallet_info(WALLET_NAME).await?;
let cloud_info = cloud_rpc.get_wallet_info(WALLET_NAME).await?;
println!("Local wallet:");
println!(" Tx count: {:?}", local_info.get("txcount"));
println!("Cloud wallet:");
println!(" Tx count: {:?}", cloud_info.get("txcount"));
println!();
println!("๐ Migration Complete!");
println!("===================");
println!(
"โ
Wallet '{}' has been migrated from local to cloud node",
WALLET_NAME
);
println!("โ
All private keys and master blinding key have been imported");
println!("โ
Blinding keys are automatically derived from the master blinding key");
println!();
if cloud_info.get("txcount").and_then(|v| v.as_u64()) == Some(0) {
println!("โ ๏ธ Note: Cloud wallet shows 0 transactions");
println!(" The cloud node needs to rescan the blockchain to see existing transactions.");
println!();
println!(" Run this command on the cloud node:");
println!(
" elements-cli -rpcwallet={} rescanblockchain",
WALLET_NAME
);
println!();
}
println!("๐ก Next steps:");
println!(
" - Rescan blockchain on cloud node: elements-cli -rpcwallet={} rescanblockchain",
WALLET_NAME
);
println!(" - Verify addresses match between local and cloud");
println!(" - Test generating a confidential address on cloud node");
println!(" - Test transaction signing on cloud node");
Ok(())
}