diff --git a/src/bin/lookup_contract_by_address.rs b/src/bin/lookup_contract_by_address.rs index a3e3943..1ebef88 100644 --- a/src/bin/lookup_contract_by_address.rs +++ b/src/bin/lookup_contract_by_address.rs @@ -63,7 +63,7 @@ async fn main() { wallet_address.clone(), rpc_command, handshake::HandshakeWallet::WalletParts { - long_address: wallet.saved.long_address.clone(), + public_key: wallet.saved.public_key.clone(), private_key: wallet.saved.private_key.clone(), }, hashmap_key, diff --git a/src/bin/lookup_mempool_tx_by_address.rs b/src/bin/lookup_mempool_tx_by_address.rs index 4cbde7b..19b2e16 100644 --- a/src/bin/lookup_mempool_tx_by_address.rs +++ b/src/bin/lookup_mempool_tx_by_address.rs @@ -144,7 +144,7 @@ async fn main() { address.clone(), rpc_command, handshake::HandshakeWallet::WalletParts { - long_address: wallet.saved.long_address.clone(), + public_key: wallet.saved.public_key.clone(), private_key: wallet.saved.private_key.clone(), }, generate_uid(), diff --git a/src/bin/lookup_remote_balance.rs b/src/bin/lookup_remote_balance.rs index cd2819c..4e366e0 100644 --- a/src/bin/lookup_remote_balance.rs +++ b/src/bin/lookup_remote_balance.rs @@ -33,14 +33,12 @@ fn extract_address(contents: &str) -> Result { } if trimmed.starts_with('{') { - // Prefer short_address for balance lookups, but accept long_address too. let value: Value = from_str(trimmed).map_err(|e| format!("Failed to parse wallet JSON: {e}"))?; let address = value .get("short_address") .and_then(|v| v.as_str()) .or_else(|| value.get("vanity_address").and_then(|v| v.as_str())) - .or_else(|| value.get("long_address").and_then(|v| v.as_str())) .ok_or_else(|| "Wallet JSON does not contain a usable address field".to_string())?; return Ok(address.trim().to_string()); } @@ -149,7 +147,7 @@ async fn main() { json.clone(), rpc_command, handshake::HandshakeWallet::WalletParts { - long_address: wallet.saved.long_address.clone(), + public_key: wallet.saved.public_key.clone(), private_key: wallet.saved.private_key.clone(), }, hashmap_key, diff --git a/src/bin/recreate_wallet.rs b/src/bin/recreate_wallet.rs index 71ffeea..74f2fc3 100644 --- a/src/bin/recreate_wallet.rs +++ b/src/bin/recreate_wallet.rs @@ -36,7 +36,7 @@ fn display_vanity_address(short_address: &str) -> Option { async fn lookup_registered_vanity_address( short_address: &str, - long_address: &str, + public_key: &str, private_key: &str, ) -> Option { let hashmap_key = generate_uid(); @@ -53,7 +53,7 @@ async fn lookup_registered_vanity_address( short_address.to_string(), rpc_command, handshake::HandshakeWallet::WalletParts { - long_address: long_address.to_string(), + public_key: public_key.to_string(), private_key: private_key.to_string(), }, hashmap_key, @@ -209,17 +209,14 @@ async fn main() { match Wallet::regenerate_public_key(&private_key) { Ok(public_key_bytes) => { let public_key = blockchain::encode(public_key_bytes.clone()); - let long_address = Wallet::generate_address(&public_key); - let long_address_bytes = Wallet::long_address_to_bytes(long_address.clone()); let short_address_bytes = - Wallet::long_address_bytes_to_short_address_bytes(&long_address_bytes) + Wallet::public_key_bytes_to_short_address_bytes(&public_key_bytes) .expect("Failed to derive short address bytes"); let short_address = Wallet::bytes_to_short_address(&short_address_bytes) .expect("Failed to encode short address"); let vanity_address = - lookup_registered_vanity_address(&short_address, &long_address, &private_key).await; + lookup_registered_vanity_address(&short_address, &public_key, &private_key).await; let saved_wallet = SavedWallet { - long_address, short_address, vanity_address, public_key, diff --git a/src/bin/recreate_wallet_from_image.rs b/src/bin/recreate_wallet_from_image.rs index 35b0ab1..d1d089a 100644 --- a/src/bin/recreate_wallet_from_image.rs +++ b/src/bin/recreate_wallet_from_image.rs @@ -34,7 +34,7 @@ fn display_vanity_address(short_address: &str) -> Option { async fn lookup_registered_vanity_address( short_address: &str, - long_address: &str, + public_key: &str, private_key: &str, ) -> Option { let hashmap_key = generate_uid(); @@ -51,7 +51,7 @@ async fn lookup_registered_vanity_address( short_address.to_string(), rpc_command, handshake::HandshakeWallet::WalletParts { - long_address: long_address.to_string(), + public_key: public_key.to_string(), private_key: private_key.to_string(), }, hashmap_key, @@ -246,17 +246,14 @@ async fn main() { match Wallet::regenerate_public_key(&private_key) { Ok(public_key_bytes) => { let public_key = blockchain::encode(public_key_bytes.clone()); - let long_address = Wallet::generate_address(&public_key); - let long_address_bytes = Wallet::long_address_to_bytes(long_address.clone()); let short_address_bytes = - Wallet::long_address_bytes_to_short_address_bytes(&long_address_bytes) + Wallet::public_key_bytes_to_short_address_bytes(&public_key_bytes) .expect("Failed to derive short address bytes"); let short_address = Wallet::bytes_to_short_address(&short_address_bytes) .expect("Failed to encode short address"); let vanity_address = - lookup_registered_vanity_address(&short_address, &long_address, &private_key).await; + lookup_registered_vanity_address(&short_address, &public_key, &private_key).await; let saved_wallet = SavedWallet { - long_address, short_address, vanity_address, public_key, diff --git a/src/bin/register_wallet.rs b/src/bin/register_wallet.rs index d4a0f0e..6a646df 100644 --- a/src/bin/register_wallet.rs +++ b/src/bin/register_wallet.rs @@ -35,9 +35,8 @@ async fn main() { } }; - // The peer receives both addresses, but the signature proves they belong together. + // The peer receives the short address and raw public key; the signature proves they belong together. let short_address = wallet.saved.short_address.clone(); - let long_address = wallet.saved.long_address.clone(); let short_address_bytes = match Wallet::short_address_to_bytes(&short_address) { Some(bytes) => bytes, None => { @@ -45,20 +44,28 @@ async fn main() { return; } }; - let long_address_bytes = Wallet::long_address_to_bytes(long_address.clone()); + let public_key_bytes = match Wallet::normalize_saved_public_key_bytes(&wallet.saved.public_key) + { + Some(bytes) => bytes, + None => { + eprintln!("Failed to decode public key from the wallet."); + return; + } + }; - // The signed payload mirrors the binary request body: command byte, short address, long address. + // The signed payload mirrors the binary request body: command byte, short address, public key. let mut signed_payload = - Vec::with_capacity(1 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + Wallet::ADDRESS_BYTES_LENGTH); + Vec::with_capacity(1 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + Wallet::PUBLIC_KEY_LENGTH); signed_payload.push(RPC_REGISTER_WALLET); signed_payload.extend_from_slice(&short_address_bytes); - signed_payload.extend_from_slice(&long_address_bytes); + signed_payload.extend_from_slice(&public_key_bytes); let payload_hash = skein_256_hash_bytes(&signed_payload); let signature = Wallet::sign_transaction(&payload_hash, &wallet.saved.private_key).await; // sending_request encodes command 38 from this pipe-delimited payload. - let json = format!("{short_address}|{long_address}|{signature}"); + let public_key_hex = blockchain::encode(&public_key_bytes); + let json = format!("{short_address}|{public_key_hex}|{signature}"); let rpc_command = 38; let connections = get_connections().await; diff --git a/src/bin/verify_address.rs b/src/bin/verify_address.rs index 6a854c3..1193436 100644 --- a/src/bin/verify_address.rs +++ b/src/bin/verify_address.rs @@ -30,13 +30,12 @@ fn extract_address(contents: &str) -> Result { } if trimmed.starts_with('{') { - // Prefer the short address when present, but accept long_address for wallet validation too. + // Wallet JSON address validation uses the canonical short address. let value: Value = from_str(trimmed).map_err(|e| format!("Failed to parse wallet JSON: {e}"))?; let address = value .get("short_address") .and_then(|v| v.as_str()) - .or_else(|| value.get("long_address").and_then(|v| v.as_str())) .ok_or_else(|| "Wallet JSON does not contain a usable address field".to_string())?; return Ok(address.trim().to_string()); } @@ -100,9 +99,7 @@ async fn main() { } }; - // Verify either a long wallet address or a current-network short address. - let message_hash = Wallet::wallet_validation(address.trim()).await - || Wallet::short_address_validation(address.trim()); + let message_hash = Wallet::short_address_validation(address.trim()); println!("{message_hash}"); } diff --git a/src/bin/verify_message.rs b/src/bin/verify_message.rs index 460c12d..d1db985 100644 --- a/src/bin/verify_message.rs +++ b/src/bin/verify_message.rs @@ -1,8 +1,11 @@ use blockchain::common::cli_prompts::prompt_visible; +use blockchain::common::network_paths_and_settings::block_extension_and_paths; use blockchain::common::skein::skein_256_hash_data; use blockchain::env; use blockchain::from_str; use blockchain::read_to_string; +use blockchain::records::wallet_registry::resolve_pubkey_from_short_address; +use blockchain::sled; use blockchain::tilde; use blockchain::wallets::structures::Wallet; use blockchain::Value; @@ -41,13 +44,12 @@ fn extract_address(contents: &str) -> Result { } if trimmed.starts_with('{') { - // Signature verification prefers the long address but can also verify with a short address. + // Signature verification uses the canonical short address. let value: Value = from_str(trimmed).map_err(|e| format!("Failed to parse wallet JSON: {e}"))?; let address = value - .get("long_address") + .get("short_address") .and_then(|v| v.as_str()) - .or_else(|| value.get("short_address").and_then(|v| v.as_str())) .ok_or_else(|| "Wallet JSON does not contain a usable address field".to_string())?; return Ok(address.trim().to_string()); } @@ -140,7 +142,31 @@ async fn main() { // Hash the message exactly as sign_message does before verifying the wallet signature. let message_hash = skein_256_hash_data(message.as_str()); - let signature = Wallet::verify_transaction(&message_hash, &signature, address.trim()).await; + let ( + _network_name, + _padded_base_coin, + _suffix, + _torrent_path, + _wallet_path, + _block_path, + db_path, + _balance_path, + _log_path, + ) = block_extension_and_paths(); + let db = match sled::open(&db_path) { + Ok(db) => db, + Err(err) => { + eprintln!("Failed to open wallet registry database: {err}"); + return; + } + }; + let signature = match resolve_pubkey_from_short_address(&db, address.trim()) { + Ok(Some(pubkey)) => { + Wallet::verify_transaction_with_public_key_bytes(&message_hash, &signature, &pubkey) + .await + } + _ => false, + }; if signature { println!("valid signature"); diff --git a/src/records/memory/connections.rs b/src/records/memory/connections.rs index 513d4e3..c4516f3 100644 --- a/src/records/memory/connections.rs +++ b/src/records/memory/connections.rs @@ -213,7 +213,7 @@ impl Connection { port, stream, client_type, - wallet_address, + wallet_public_key, command_map, } = params; @@ -250,8 +250,7 @@ impl Connection { return false; } - let address = Wallet::long_address_to_bytes(wallet_address); - if address.len() != Wallet::ADDRESS_BYTES_LENGTH { + if wallet_public_key.len() != Wallet::PUBLIC_KEY_LENGTH { return false; } let connection_info = ConnectionInfo::new( @@ -260,12 +259,17 @@ impl Connection { port, stream.clone(), client_type.as_bytes(), - address.clone(), + wallet_public_key.clone(), ); self.connection_map.insert(connection_key, connection_info); if client_type == ClientType::Miner { - spawn_monitor_update(ip.clone(), MONITOR_ACTION_ADD, address.clone(), port); + spawn_monitor_update( + ip.clone(), + MONITOR_ACTION_ADD, + wallet_public_key.clone(), + port, + ); Connection::client_checkup(stream, connection_type, ip, port, command_map); } true @@ -290,7 +294,7 @@ impl Connection { spawn_monitor_update( ip.clone(), MONITOR_ACTION_REMOVE, - connection_info.wallet_address.clone(), + connection_info.wallet_public_key.clone(), port, ); } @@ -509,7 +513,7 @@ impl Connection { && connection_key.port == port && ClientType::from_bytes(&info.client_type) == Some(ClientType::Miner) { - Some(info.wallet_address.clone()) + Some(info.wallet_public_key.clone()) } else { None } @@ -615,7 +619,7 @@ impl Connection { } } -fn spawn_monitor_update(ip: String, action: u8, peer_wallet_bytes: Vec, port: u16) { +fn spawn_monitor_update(ip: String, action: u8, peer_public_key: Vec, port: u16) { tokio::spawn(async move { let context = { let guard = RECONNECT_CONTEXT.lock().await; @@ -624,8 +628,8 @@ fn spawn_monitor_update(ip: String, action: u8, peer_wallet_bytes: Vec, port let Some(context) = context else { return; }; - let peer_long = Wallet::bytes_to_long_address(peer_wallet_bytes); - let Some(monitored_address) = Wallet::normalize_to_short_address(&peer_long) else { + let Some(monitored_address) = Wallet::public_key_bytes_to_short_address(&peer_public_key) + else { return; }; let monitoring_address = context.wallet.saved.short_address.clone(); diff --git a/src/records/memory/network_mapping/add.rs b/src/records/memory/network_mapping/add.rs index fcbe725..05f8d91 100644 --- a/src/records/memory/network_mapping/add.rs +++ b/src/records/memory/network_mapping/add.rs @@ -22,7 +22,10 @@ impl NodeInfo { return; } }; - let modified_by_bytes = Wallet::long_address_to_bytes(edit.modified_by.clone()); + let modified_by_bytes = match Wallet::short_address_to_bytes(&edit.modified_by) { + Some(bytes) => bytes, + None => vec![0u8; Wallet::SHORT_ADDRESS_BYTES_LENGTH], + }; let modified_timestamp_bytes = edit.modified_timestamp.to_le_bytes(); let modified_signature_bytes = decode(&edit.modified_signature).unwrap(); let connections_lock = CONNECTIONS.read().await; @@ -90,7 +93,7 @@ impl NodeInfo { // current timestamp so they can be propagated as fresh node events. if edit.ip == remote_ip { edit.modified_timestamp = current_timestamp; - edit.modified_by = wallet.saved.long_address.clone(); + edit.modified_by = wallet.saved.short_address.clone(); edit.modified_signature = Self::added_signature(&edit.address, &edit.ip, current_timestamp, &wallet).await; } @@ -107,8 +110,19 @@ impl NodeInfo { // Every add/delete edit is signed, so the network map accepts // only node changes backed by a valid wallet signature. - if !Wallet::verify_transaction(&hashed_data, &edit.modified_signature, &edit.modified_by) - .await + let Ok(Some(pubkey)) = crate::records::wallet_registry::resolve_pubkey_from_short_address( + &db, + &edit.modified_by, + ) else { + return RpcResponse::Binary(b"Error: Could not validate signature".to_vec()); + }; + + if !Wallet::verify_transaction_with_public_key_bytes( + &hashed_data, + &edit.modified_signature, + &pubkey, + ) + .await { return RpcResponse::Binary(b"Error: Could not validate signature".to_vec()); } diff --git a/src/records/memory/network_mapping/monitor.rs b/src/records/memory/network_mapping/monitor.rs index 48f00a4..88dfddc 100644 --- a/src/records/memory/network_mapping/monitor.rs +++ b/src/records/memory/network_mapping/monitor.rs @@ -129,7 +129,6 @@ impl NodeInfo { else { return false; }; - let signer = Wallet::bytes_to_long_address(pubkey); let data = format!( "{}{}{}{}{}", edit.action, @@ -139,7 +138,12 @@ impl NodeInfo { edit.modified_timestamp ); let hashed_data = skein_256_hash_data(&data); - Wallet::verify_transaction(&hashed_data, &edit.modified_signature, &signer).await + Wallet::verify_transaction_with_public_key_bytes( + &hashed_data, + &edit.modified_signature, + &pubkey, + ) + .await } pub async fn add_monitor(params: MonitorAddressParams) -> RpcResponse { diff --git a/src/records/memory/network_mapping/queries.rs b/src/records/memory/network_mapping/queries.rs index b18e5c7..5b182a4 100644 --- a/src/records/memory/network_mapping/queries.rs +++ b/src/records/memory/network_mapping/queries.rs @@ -67,7 +67,8 @@ impl NodeInfo { }; let ip_bytes = ip_to_binary(&node_info.ip); let blocks_mined = node_info.blocks_mined; - let added_by_bytes = Wallet::long_address_to_bytes(node_info.added_by.to_string()); + let added_by_bytes = + Wallet::short_address_to_bytes(&node_info.added_by).unwrap_or_default(); let added_timestamp_bytes = node_info.added_timestamp.to_le_bytes(); let deleted_timestamp_bytes = node_info.deleted_timestamp.to_le_bytes(); @@ -82,7 +83,11 @@ impl NodeInfo { data.extend_from_slice(&address_bytes); data.extend_from_slice(&ip_bytes); data.push(blocks_mined); - data.extend_from_slice(&added_by_bytes); + if added_by_bytes.len() == Wallet::SHORT_ADDRESS_BYTES_LENGTH { + data.extend_from_slice(&added_by_bytes); + } else { + data.extend_from_slice(&[0u8; Wallet::SHORT_ADDRESS_BYTES_LENGTH]); + } data.extend_from_slice(&added_timestamp_bytes); data.extend_from_slice(&added_signature_bytes); data.extend_from_slice(&deleted_timestamp_bytes); @@ -107,7 +112,7 @@ impl NodeInfo { ) -> String { // Node edits are signed over address, IP, signer, and timestamp // so peers can independently verify the advertised change. - let added_by = wallet.saved.long_address.clone(); + let added_by = wallet.saved.short_address.clone(); let private_key = &wallet.saved.private_key; let data = format!("{address}{ip}{added_by}{current_timestamp}"); diff --git a/src/records/memory/network_mapping/structs.rs b/src/records/memory/network_mapping/structs.rs index cf7fff8..c587a5d 100644 --- a/src/records/memory/network_mapping/structs.rs +++ b/src/records/memory/network_mapping/structs.rs @@ -14,7 +14,8 @@ pub const NODE_ADDRESS_OFFSET: usize = 0; pub const NODE_IP_OFFSET: usize = NODE_ADDRESS_OFFSET + Wallet::SHORT_ADDRESS_BYTES_LENGTH; pub const NODE_BLOCKS_MINED_OFFSET: usize = NODE_IP_OFFSET + NODE_IP_BYTES; pub const NODE_ADDED_BY_OFFSET: usize = NODE_BLOCKS_MINED_OFFSET + NODE_BLOCKS_MINED_BYTES; -pub const NODE_ADDED_TIMESTAMP_OFFSET: usize = NODE_ADDED_BY_OFFSET + Wallet::ADDRESS_BYTES_LENGTH; +pub const NODE_ADDED_TIMESTAMP_OFFSET: usize = + NODE_ADDED_BY_OFFSET + Wallet::SHORT_ADDRESS_BYTES_LENGTH; pub const NODE_ADDED_SIGNATURE_OFFSET: usize = NODE_ADDED_TIMESTAMP_OFFSET + NODE_TIMESTAMP_BYTES; pub const NODE_DELETED_TIMESTAMP_OFFSET: usize = NODE_ADDED_SIGNATURE_OFFSET + Wallet::SIGNATURE_LENGTH; diff --git a/src/records/memory/structs.rs b/src/records/memory/structs.rs index bbdefac..3e7f39d 100644 --- a/src/records/memory/structs.rs +++ b/src/records/memory/structs.rs @@ -15,7 +15,7 @@ pub struct ConnectionInfo { pub port: u16, pub stream: Arc>, pub client_type: Vec, - pub wallet_address: Vec, + pub wallet_public_key: Vec, } // ConnectionKey is the stable lookup key used inside the in-memory connection map. @@ -34,7 +34,7 @@ pub struct StoreConnectionParams { pub port: u16, pub stream: Arc>, pub client_type: ClientType, - pub wallet_address: String, + pub wallet_public_key: Vec, pub command_map: Arc>, } @@ -199,7 +199,7 @@ impl ConnectionInfo { port: u16, stream: Arc>, client_type: Vec, - wallet_address: Vec, + wallet_public_key: Vec, ) -> Self { ConnectionInfo { connection_type, @@ -207,7 +207,7 @@ impl ConnectionInfo { port, stream, client_type, - wallet_address, + wallet_public_key, } } } diff --git a/src/rpc/client/handshake_message.rs b/src/rpc/client/handshake_message.rs index 7927d3c..da0d8bf 100644 --- a/src/rpc/client/handshake_message.rs +++ b/src/rpc/client/handshake_message.rs @@ -22,7 +22,8 @@ pub async fn prepare_handshake_message(wallet: &Wallet, message: &str) -> io::Re } }; - let address_bin = Wallet::long_address_to_bytes(wallet.saved.long_address.clone()); + let public_key_bin = Wallet::normalize_saved_public_key_bytes(&wallet.saved.public_key) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid wallet public key"))?; let message_bin = decode(message).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; let signed_bin = @@ -34,7 +35,7 @@ pub async fn prepare_handshake_message(wallet: &Wallet, message: &str) -> io::Re let mut data = Vec::with_capacity(HANDSHAKE_REQUEST_BYTES); data.extend_from_slice(&message_bin); data.extend_from_slice(&signed_bin); - data.extend_from_slice(&address_bin); + data.extend_from_slice(&public_key_bin); data.extend_from_slice(&time_bin); data.extend_from_slice(&ip); diff --git a/src/rpc/client/handshake_processing.rs b/src/rpc/client/handshake_processing.rs index a68747f..8bdf696 100644 --- a/src/rpc/client/handshake_processing.rs +++ b/src/rpc/client/handshake_processing.rs @@ -236,25 +236,21 @@ pub async fn process_handshake_response( let returned_message_bin = &response[..HANDSHAKE_MESSAGE_BYTES]; let returned_signed_bin = &response[HANDSHAKE_SIGNATURE_OFFSET..HANDSHAKE_ADDRESS_OFFSET]; - let returned_address_bin = &response[HANDSHAKE_ADDRESS_OFFSET..HANDSHAKE_RESPONSE_BYTES]; + let returned_public_key_bin = &response[HANDSHAKE_ADDRESS_OFFSET..HANDSHAKE_RESPONSE_BYTES]; let returned_message = encode(returned_message_bin); let returned_signed_message = encode(returned_signed_bin); - if Wallet::map_byte_to_wallet(returned_address_bin[0]).is_empty() { - return Err(io::Error::new( - io::ErrorKind::InvalidData, - "Invalid handshake wallet network byte", - )); - } - let returned_address = Wallet::bytes_to_long_address(returned_address_bin.to_vec()); - - if !Wallet::wallet_validation(&returned_address).await { + if returned_public_key_bin.len() != Wallet::PUBLIC_KEY_LENGTH { return Ok(()); } let hash = skein_256_hash_data(&returned_message); - let valid_response_signature = - Wallet::verify_transaction(&hash, &returned_signed_message, &returned_address).await; + let valid_response_signature = Wallet::verify_transaction_with_public_key_bytes( + &hash, + &returned_signed_message, + returned_public_key_bin, + ) + .await; if returned_message == "ecaf" && valid_response_signature { return Err(io::Error::other( @@ -290,7 +286,7 @@ pub async fn process_handshake_response( port, stream: Arc::clone(&stream), client_type: ClientType::Miner, - wallet_address: returned_address.clone(), + wallet_public_key: returned_public_key_bin.to_vec(), command_map: params.map.clone(), }) { return Err(io::Error::other( diff --git a/src/rpc/client/register_wallet.rs b/src/rpc/client/register_wallet.rs index e3d1a62..bca348e 100644 --- a/src/rpc/client/register_wallet.rs +++ b/src/rpc/client/register_wallet.rs @@ -19,16 +19,14 @@ pub async fn register_connected_wallet( ) -> Result<(), String> { let short_address_bytes = Wallet::short_address_to_bytes(&wallet.saved.short_address) .ok_or_else(|| "Failed to derive short address bytes from wallet".to_string())?; - let long_address_bytes = Wallet::long_address_to_bytes(wallet.saved.long_address.clone()); - if long_address_bytes.len() != Wallet::ADDRESS_BYTES_LENGTH { - return Err("Startup wallet long address was invalid".to_string()); - } + let public_key_bytes = Wallet::normalize_saved_public_key_bytes(&wallet.saved.public_key) + .ok_or_else(|| "Startup wallet public key was invalid".to_string())?; let mut signed_payload = - Vec::with_capacity(1 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + Wallet::ADDRESS_BYTES_LENGTH); + Vec::with_capacity(1 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + Wallet::PUBLIC_KEY_LENGTH); signed_payload.push(RPC_REGISTER_WALLET); signed_payload.extend_from_slice(&short_address_bytes); - signed_payload.extend_from_slice(&long_address_bytes); + signed_payload.extend_from_slice(&public_key_bytes); let payload_hash = skein_256_hash_bytes(&signed_payload); let signature = Wallet::sign_transaction(&payload_hash, &wallet.saved.private_key).await; @@ -42,13 +40,13 @@ pub async fn register_connected_wallet( let mut message = Vec::with_capacity( 1 + 3 + Wallet::SHORT_ADDRESS_BYTES_LENGTH - + Wallet::ADDRESS_BYTES_LENGTH + + Wallet::PUBLIC_KEY_LENGTH + Wallet::SIGNATURE_LENGTH, ); message.push(RPC_REGISTER_WALLET); message.extend_from_slice(&hashmap_key); message.extend_from_slice(&short_address_bytes); - message.extend_from_slice(&long_address_bytes); + message.extend_from_slice(&public_key_bytes); message.extend_from_slice(&signature_bytes); RpcResponse::send_raw(&stream, Some(&connections_key), &message).await; diff --git a/src/rpc/commands/add_network_node.rs b/src/rpc/commands/add_network_node.rs index c5619f1..7253831 100644 --- a/src/rpc/commands/add_network_node.rs +++ b/src/rpc/commands/add_network_node.rs @@ -32,14 +32,14 @@ pub async fn add_network_node( read_bytes_from_stream::read_ip_from_stream(connections_key, stream_locked.clone()).await?; let added_by_bytes = read_bytes_from_stream::read_usize_from_stream( connections_key, - Wallet::ADDRESS_BYTES_LENGTH, + Wallet::SHORT_ADDRESS_BYTES_LENGTH, stream_locked.clone(), ) .await?; let added_by = if added_by_bytes.iter().all(|&byte| byte == 0) { String::new() } else { - Wallet::bytes_to_long_address(added_by_bytes) + Wallet::bytes_to_short_address(&added_by_bytes).unwrap_or_default() }; let added_timestamp = read_bytes_from_stream::read_u64_from_stream(connections_key, stream_locked.clone()) diff --git a/src/rpc/commands/block_peer_ip.rs b/src/rpc/commands/block_peer_ip.rs index dd3ecb2..4947f2e 100644 --- a/src/rpc/commands/block_peer_ip.rs +++ b/src/rpc/commands/block_peer_ip.rs @@ -11,7 +11,9 @@ pub async fn block_peer( ) -> RpcResponse { // Peer blocking is restricted to the local node owner, proven by a // signature from the locally loaded wallet over the target IP string. - if Wallet::verify_transaction(&ip, &signature, &wallet.saved.long_address).await { + let public_key = + Wallet::normalize_saved_public_key_bytes(&wallet.saved.public_key).unwrap_or_default(); + if Wallet::verify_transaction_with_public_key_bytes(&ip, &signature, &public_key).await { let tree = db.open_tree("blocked_peers").unwrap(); let key = ip.clone(); let value = b"blocked"; diff --git a/src/rpc/commands/delete_network_node.rs b/src/rpc/commands/delete_network_node.rs index 0bf4ff0..42b9f55 100644 --- a/src/rpc/commands/delete_network_node.rs +++ b/src/rpc/commands/delete_network_node.rs @@ -30,9 +30,13 @@ pub async fn delete_network_node( .ok_or_else(|| "error: Invalid short address bytes".to_string())?; let ip = read_bytes_from_stream::read_ip_from_stream(connections_key, stream_locked.clone()).await?; - let deleted_by = - read_bytes_from_stream::read_wallet_from_stream(connections_key, stream_locked.clone()) - .await?; + let deleted_by_bytes = read_bytes_from_stream::read_short_address_from_stream( + connections_key, + stream_locked.clone(), + ) + .await?; + let deleted_by = Wallet::bytes_to_short_address(&deleted_by_bytes) + .ok_or_else(|| "error: Invalid short address bytes".to_string())?; let deleted_timestamp = read_bytes_from_stream::read_u64_from_stream(connections_key, stream_locked.clone()) .await?; diff --git a/src/rpc/commands/unblock_peer_ip.rs b/src/rpc/commands/unblock_peer_ip.rs index c5a1811..06bb3cc 100644 --- a/src/rpc/commands/unblock_peer_ip.rs +++ b/src/rpc/commands/unblock_peer_ip.rs @@ -11,7 +11,9 @@ pub async fn unblock_peer( ) -> RpcResponse { // Peer unblocking is restricted to the local node owner, proven by a // signature from the locally loaded wallet over the target IP string. - if Wallet::verify_transaction(&ip, &signature, &wallet.saved.long_address).await { + let public_key = + Wallet::normalize_saved_public_key_bytes(&wallet.saved.public_key).unwrap_or_default(); + if Wallet::verify_transaction_with_public_key_bytes(&ip, &signature, &public_key).await { let tree = db.open_tree("blocked_peers").unwrap(); let key = ip.clone(); let _ = tree.remove(key); diff --git a/src/rpc/commands/validate_message.rs b/src/rpc/commands/validate_message.rs index e5b1304..66c667c 100644 --- a/src/rpc/commands/validate_message.rs +++ b/src/rpc/commands/validate_message.rs @@ -1,10 +1,16 @@ +use crate::records::wallet_registry::resolve_pubkey_from_short_address; use crate::rpc::responses::RpcResponse; +use crate::sled::Db; use crate::wallets::structures::Wallet; -pub async fn validate(message: String, signature: String, address: String) -> RpcResponse { +pub async fn validate(message: String, signature: String, address: String, db: &Db) -> RpcResponse { // Signed-message verification returns a simple text status for // lightweight external clients. - if Wallet::verify_transaction(&message, &signature, &address).await { + let Ok(Some(pubkey)) = resolve_pubkey_from_short_address(db, &address) else { + return RpcResponse::Binary(b"invalid".to_vec()); + }; + + if Wallet::verify_transaction_with_public_key_bytes(&message, &signature, &pubkey).await { let msg = "valid".to_string().as_bytes().to_vec(); RpcResponse::Binary(msg) } else { diff --git a/src/rpc/commands/wallet_register.rs b/src/rpc/commands/wallet_register.rs index 8d5d4fd..6fefb44 100644 --- a/src/rpc/commands/wallet_register.rs +++ b/src/rpc/commands/wallet_register.rs @@ -15,7 +15,7 @@ use crate::Mutex; async fn broadcast_wallet_registration( short_address: &[u8], - long_address_bytes: &[u8], + public_key_bytes: &[u8], signature: &str, map: Arc>, remote_ip: &str, @@ -54,14 +54,14 @@ async fn broadcast_wallet_registration( let mut message = Vec::with_capacity( 1 + 3 + Wallet::SHORT_ADDRESS_BYTES_LENGTH - + Wallet::ADDRESS_BYTES_LENGTH + + Wallet::PUBLIC_KEY_LENGTH + Wallet::SIGNATURE_LENGTH, ); - // Wire layout: command, UID, short address, long address, signature. + // Wire layout: command, UID, short address, public key, signature. message.push(RPC_REGISTER_WALLET); message.extend_from_slice(&hashmap_key); message.extend_from_slice(short_address); - message.extend_from_slice(long_address_bytes); + message.extend_from_slice(public_key_bytes); message.extend_from_slice(&signature_bytes); RpcResponse::send_raw(&unlocked_stream, Some(connections_key), &message).await; @@ -79,7 +79,7 @@ async fn broadcast_wallet_registration( pub async fn register( short_address_bytes: Vec, - long_address: String, + public_key_bytes: Vec, signature: String, db: &Db, map: Arc>, @@ -97,17 +97,12 @@ pub async fn register( return RpcResponse::Binary(b"0".to_vec()); } - if !Wallet::wallet_validation(&long_address).await { - return RpcResponse::Binary(b"0".to_vec()); - } - - let long_address_bytes = Wallet::long_address_to_bytes(long_address.clone()); - if long_address_bytes.len() != Wallet::ADDRESS_BYTES_LENGTH { + if public_key_bytes.len() != Wallet::PUBLIC_KEY_LENGTH { return RpcResponse::Binary(b"0".to_vec()); } let expected_short_address = - match Wallet::long_address_bytes_to_short_address_bytes(&long_address_bytes) { + match Wallet::public_key_bytes_to_short_address_bytes(&public_key_bytes) { Some(address) => address, None => return RpcResponse::Binary(b"0".to_vec()), }; @@ -119,27 +114,29 @@ pub async fn register( } let mut signed_payload = - Vec::with_capacity(1 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + Wallet::ADDRESS_BYTES_LENGTH); + Vec::with_capacity(1 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + Wallet::PUBLIC_KEY_LENGTH); signed_payload.push(RPC_REGISTER_WALLET); signed_payload.extend_from_slice(&short_address_bytes); - signed_payload.extend_from_slice(&long_address_bytes); + signed_payload.extend_from_slice(&public_key_bytes); // Registrations are signed over the exact command payload so peers // can verify rebroadcasted registrations without trusting the sender. let payload_hash = skein_256_hash_bytes(&signed_payload); - if !Wallet::verify_transaction(&payload_hash, &signature, &long_address).await { + if !Wallet::verify_transaction_with_public_key_bytes( + &payload_hash, + &signature, + &public_key_bytes, + ) + .await + { return RpcResponse::Binary(b"0".to_vec()); } - let public_key_bytes = &long_address_bytes[1..]; - - // The registry stores short address -> public key; the long-address - // network byte is not part of the Falcon public key. - match register_short_address(db, &short_address_bytes, public_key_bytes) { + match register_short_address(db, &short_address_bytes, &public_key_bytes) { Ok(WalletRegistrationResult::Inserted) => { broadcast_wallet_registration( &short_address_bytes, - &long_address_bytes, + &public_key_bytes, &signature, map, &remote_ip, diff --git a/src/rpc/handshake_constants.rs b/src/rpc/handshake_constants.rs index f41ec06..4c9712d 100644 --- a/src/rpc/handshake_constants.rs +++ b/src/rpc/handshake_constants.rs @@ -6,8 +6,8 @@ pub const HANDSHAKE_IP_BYTES: usize = 18; pub const HANDSHAKE_SIGNATURE_OFFSET: usize = HANDSHAKE_MESSAGE_BYTES; pub const HANDSHAKE_ADDRESS_OFFSET: usize = HANDSHAKE_SIGNATURE_OFFSET + Wallet::SIGNATURE_LENGTH; -pub const HANDSHAKE_TIME_OFFSET: usize = HANDSHAKE_ADDRESS_OFFSET + Wallet::ADDRESS_BYTES_LENGTH; +pub const HANDSHAKE_TIME_OFFSET: usize = HANDSHAKE_ADDRESS_OFFSET + Wallet::PUBLIC_KEY_LENGTH; pub const HANDSHAKE_IP_OFFSET: usize = HANDSHAKE_TIME_OFFSET + HANDSHAKE_TIME_BYTES; pub const HANDSHAKE_REQUEST_BYTES: usize = HANDSHAKE_IP_OFFSET + HANDSHAKE_IP_BYTES; -pub const HANDSHAKE_RESPONSE_BYTES: usize = HANDSHAKE_ADDRESS_OFFSET + Wallet::ADDRESS_BYTES_LENGTH; +pub const HANDSHAKE_RESPONSE_BYTES: usize = HANDSHAKE_ADDRESS_OFFSET + Wallet::PUBLIC_KEY_LENGTH; diff --git a/src/rpc/read_bytes_from_stream.rs b/src/rpc/read_bytes_from_stream.rs index 3ff92e0..63e7608 100644 --- a/src/rpc/read_bytes_from_stream.rs +++ b/src/rpc/read_bytes_from_stream.rs @@ -151,18 +151,15 @@ pub async fn read_usize_from_stream( Ok(buffer) } -pub async fn read_wallet_from_stream( +pub async fn read_public_key_from_stream( key: &str, stream_locked: Arc>, -) -> Result { - // Wallet addresses use a fixed binary length on the wire and are - // decoded into their CLTC string form here. +) -> Result, String> { let mut stream = stream_locked.lock().await; - let mut buffer = vec![0u8; Wallet::ADDRESS_BYTES_LENGTH]; + let mut buffer = vec![0u8; Wallet::PUBLIC_KEY_LENGTH]; read_exact_from_stream(key, &mut stream, &mut buffer).await?; drop(stream); - let address = Wallet::bytes_to_long_address(buffer.to_vec()); - Ok(address) + Ok(buffer) } pub async fn read_short_address_from_stream( diff --git a/src/rpc/server/connection_memory_manager.rs b/src/rpc/server/connection_memory_manager.rs index 403b601..8a54878 100644 --- a/src/rpc/server/connection_memory_manager.rs +++ b/src/rpc/server/connection_memory_manager.rs @@ -28,7 +28,7 @@ pub async fn write_to_memory( received_ip_port: &str, stream: Arc>, client_type: &str, - wallet: &str, + wallet_public_key: Vec, command_map: Arc>, ) -> String { // Reject unknown connection labels before the connection manager is @@ -52,7 +52,7 @@ pub async fn write_to_memory( port, stream: stream.clone(), client_type, - wallet_address: wallet.to_string(), + wallet_public_key, command_map, }); diff --git a/src/rpc/server/handshake.rs b/src/rpc/server/handshake.rs index 415560f..de3d84b 100644 --- a/src/rpc/server/handshake.rs +++ b/src/rpc/server/handshake.rs @@ -90,7 +90,7 @@ pub async fn handle_handshake( let Ok(( received_message, received_signed_message, - received_address, + received_public_key, hash, received_ip, peer_time, @@ -116,7 +116,7 @@ pub async fn handle_handshake( incoming_connections, hash: &hash, received_signed_message: &received_signed_message, - received_address: &received_address, + received_address: &received_public_key, received_ip: &received_ip, }) .await @@ -177,12 +177,20 @@ pub async fn handle_handshake( } } + let received_public_key_bytes = match crate::decode(&received_public_key) { + Ok(bytes) if bytes.len() == Wallet::PUBLIC_KEY_LENGTH => bytes, + _ => { + drop_failed_handshake(&stream).await; + return; + } + }; + // write to memory let connections_key = write_to_memory( &received_ip, stream.clone(), connection_type, - &received_address, + received_public_key_bytes, map.clone(), ) .await; @@ -202,7 +210,7 @@ pub async fn handle_handshake( connection_type: connection_type.to_string(), wallet: wallet.clone(), map, - returned_address: received_address.clone(), + returned_address: received_public_key.clone(), }; if combine_and_send_data(params).await.is_ok() && is_miner { complete_incoming_miner_setup( diff --git a/src/rpc/server/handshake_processing.rs b/src/rpc/server/handshake_processing.rs index 282485e..ec5d53b 100644 --- a/src/rpc/server/handshake_processing.rs +++ b/src/rpc/server/handshake_processing.rs @@ -48,14 +48,7 @@ pub async fn parse_received_data( let received_message = encode(&buffer[..HANDSHAKE_MESSAGE_BYTES]); let received_signed_message = encode(&buffer[HANDSHAKE_SIGNATURE_OFFSET..HANDSHAKE_ADDRESS_OFFSET]); - let received_address_bytes = &buffer[HANDSHAKE_ADDRESS_OFFSET..HANDSHAKE_TIME_OFFSET]; - - // Long wallet addresses carry the network byte at the front, so - // reject the handshake before rebuilding an address from bad bytes. - if Wallet::map_byte_to_wallet(received_address_bytes[0]).is_empty() { - return Err("error: Invalid handshake wallet network byte".to_string()); - } - let received_address = Wallet::bytes_to_long_address(received_address_bytes.to_vec()); + let received_public_key = encode(&buffer[HANDSHAKE_ADDRESS_OFFSET..HANDSHAKE_TIME_OFFSET]); // The timestamp and announced ip:port are kept as raw protocol bytes // until this point so the offsets remain explicit. @@ -70,7 +63,7 @@ pub async fn parse_received_data( Ok(( received_message, received_signed_message, - received_address, + received_public_key, hash, received_ip, peer_time, @@ -81,8 +74,7 @@ pub async fn generate_and_sign_message( connection_type: &str, wallet: &Wallet, ) -> Result<(String, String, String), String> { - // get the wallet info so we can sign our return message - let address = wallet.saved.long_address.clone(); + let public_key = wallet.saved.public_key.clone(); // if miner face is the return message, used because its hex so compressed better // otherwise face spelledbackwards is used @@ -93,7 +85,7 @@ pub async fn generate_and_sign_message( let hashed = skein_256_hash_data(message); let signed_message = Wallet::sign_transaction(&hashed, &wallet.saved.private_key).await; Ok(( - address.to_string(), + public_key.to_string(), message.to_string(), signed_message.to_string(), )) @@ -104,7 +96,7 @@ pub async fn generate_and_sign_message( let hashed = skein_256_hash_data(message); let signed_message = Wallet::sign_transaction(&hashed, &wallet.saved.private_key).await; Ok(( - address.to_string(), + public_key.to_string(), message.to_string(), signed_message.to_string(), )) @@ -113,22 +105,23 @@ pub async fn generate_and_sign_message( pub async fn return_handshake( stream: Arc>, - address: &str, + public_key: &str, message: &str, signed_message: &str, connections_key: &str, ) -> Result<(), String> { // convert to binary/bytes - let address_bin = Wallet::long_address_to_bytes(address.to_string()); + let public_key_bin = Wallet::normalize_saved_public_key_bytes(public_key) + .ok_or_else(|| "error: Invalid public key".to_string())?; let message_bin = decode(message).expect("Failed to decode message"); let signed_bin = decode(signed_message).expect("Failed to decode signed message"); let mut data = Vec::with_capacity(HANDSHAKE_RESPONSE_BYTES); // Response frames mirror the request layout: challenge, signature, - // then the wallet address that signed the challenge. + // then the public key that signed the challenge. data.extend_from_slice(&message_bin); data.extend_from_slice(&signed_bin); - data.extend(&address_bin); + data.extend(&public_key_bin); // get stream lock let mut stream_guard = stream.lock().await; @@ -164,13 +157,13 @@ pub async fn combine_and_send_data(params: CombineAndSendDataParams) -> Result<( error!("Failed: {err}"); return Err(err); } - let (address, message, signed_message) = result.unwrap(); + let (public_key, message, signed_message) = result.unwrap(); // The handshake response must complete before the command loop is // spawned, otherwise the peer may start reading command data early. if let Err(err) = return_handshake( stream.clone(), - &address, + &public_key, &message, &signed_message, &connections_key, diff --git a/src/rpc/server/handshake_verifications.rs b/src/rpc/server/handshake_verifications.rs index 3e67278..c435bc5 100644 --- a/src/rpc/server/handshake_verifications.rs +++ b/src/rpc/server/handshake_verifications.rs @@ -108,7 +108,7 @@ pub async fn verify_handshake( _map: Arc>, hash: &str, received_signed_message: &str, - received_address: &str, + received_public_key: &str, ) -> bool { // Signature verification proves the peer controls the wallet address // embedded in the handshake request. @@ -116,7 +116,13 @@ pub async fn verify_handshake( let padded_bytes = [hashmap_key[0], hashmap_key[1], hashmap_key[2], 0]; let uid = u32::from_le_bytes(padded_bytes); - if !Wallet::verify_transaction(hash, received_signed_message, received_address).await { + if !Wallet::verify_transaction_with_public_key( + hash, + received_signed_message, + received_public_key, + ) + .await + { let response_bytes = RpcResponse::Binary({ "error: Handshake failed: Invalid handshake" .to_string() diff --git a/src/rpc/server/rpc_command_loop.rs b/src/rpc/server/rpc_command_loop.rs index 5b25145..b8be218 100644 --- a/src/rpc/server/rpc_command_loop.rs +++ b/src/rpc/server/rpc_command_loop.rs @@ -483,7 +483,7 @@ pub async fn start_loop( ) .await?; let message = binary_to_string(message_bytes); - let address = read_bytes_from_stream::read_wallet_from_stream( + let address = read_bytes_from_stream::read_short_address_string_from_stream( &connections_key, stream_locked.clone(), ) @@ -495,7 +495,7 @@ pub async fn start_loop( .await?; let result = - commands::validate_message::validate(message, address, signature).await; + commands::validate_message::validate(message, signature, address, &db).await; result .send(&stream_locked, Some(&connections_key), uid) .await; @@ -724,7 +724,7 @@ pub async fn start_loop( stream_locked.clone(), ) .await?; - let long_address = read_bytes_from_stream::read_wallet_from_stream( + let public_key = read_bytes_from_stream::read_public_key_from_stream( &connections_key, stream_locked.clone(), ) @@ -737,7 +737,7 @@ pub async fn start_loop( let result = commands::wallet_register::register( short_address, - long_address, + public_key, signature, &db, map.clone(), diff --git a/src/standalone_tools/connections/handshake.rs b/src/standalone_tools/connections/handshake.rs index 3ed6229..dedcbc5 100644 --- a/src/standalone_tools/connections/handshake.rs +++ b/src/standalone_tools/connections/handshake.rs @@ -25,7 +25,7 @@ pub enum HandshakeWallet { }, // Wallet recovery tools already have the address and private key before the wallet is saved. WalletParts { - long_address: String, + public_key: String, private_key: String, }, } @@ -38,7 +38,7 @@ pub async fn connect_and_handshake( hashmap_key: Byte3, ) -> Result, io::Error> { // Resolve the wallet material before opening the stream so the handshake can sign its challenge. - let (long_address, private_key) = match wallet_source { + let (public_key, private_key) = match wallet_source { HandshakeWallet::WalletKey { encryption_key, wallet_path, @@ -46,12 +46,12 @@ pub async fn connect_and_handshake( let wallet = Wallet::try_obtain_wallet(encryption_key, Some(&wallet_path)) .await .map_err(io::Error::other)?; - (wallet.saved.long_address, wallet.saved.private_key) + (wallet.saved.public_key, wallet.saved.private_key) } HandshakeWallet::WalletParts { - long_address, + public_key, private_key, - } => (long_address, private_key), + } => (public_key, private_key), }; let stream = TcpStream::connect(addr).await?; @@ -61,7 +61,7 @@ pub async fn connect_and_handshake( stream, json, rpc_command, - long_address, + public_key, private_key, hashmap_key, ) @@ -81,7 +81,7 @@ async fn perform_handshake( mut stream: TcpStream, json: String, rpc_command: usize, - address: String, + public_key: String, private_key: String, hashmap_key: Byte3, ) -> Result, io::Error> { @@ -93,7 +93,8 @@ async fn perform_handshake( let signed_message = Wallet::sign_transaction(&hash, &private_key).await; // The peer expects the handshake as fixed-width binary fields in this order. - let address_bin = Wallet::long_address_to_bytes(address); + let public_key_bin = Wallet::normalize_saved_public_key_bytes(&public_key) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid wallet public key"))?; let message_bin = decode(message).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; let signed_bin = @@ -105,7 +106,7 @@ async fn perform_handshake( // Build the complete client handshake packet. data.extend_from_slice(&message_bin); data.extend_from_slice(&signed_bin); - data.extend_from_slice(&address_bin); + data.extend_from_slice(&public_key_bin); data.extend_from_slice(&time_bin); data.extend_from_slice(&ip); @@ -172,24 +173,27 @@ async fn perform_handshake( let response = &received; let returned_message_bin = &response[..HANDSHAKE_MESSAGE_BYTES]; let returned_signed_bin = &response[HANDSHAKE_SIGNATURE_OFFSET..HANDSHAKE_ADDRESS_OFFSET]; - let returned_address_bin = &response[HANDSHAKE_ADDRESS_OFFSET..HANDSHAKE_RESPONSE_BYTES]; + let returned_public_key_bin = &response[HANDSHAKE_ADDRESS_OFFSET..HANDSHAKE_RESPONSE_BYTES]; // Convert the returned binary fields into the formats used by wallet signature verification. let returned_message = encode(returned_message_bin); let returned_signed_message = encode(returned_signed_bin); - let complete_returned_address = Wallet::bytes_to_long_address(returned_address_bin.to_vec()); - if complete_returned_address.is_empty() { + if returned_public_key_bin.len() != Wallet::PUBLIC_KEY_LENGTH { return Err(io::Error::new( io::ErrorKind::InvalidData, - "Handshake returned an invalid wallet address", + "Handshake returned an invalid public key", )); } // The server challenge must be the expected marker and its signature must match its address. let hash = skein_256_hash_data(&returned_message); if returned_message == "ecaf" { - if Wallet::verify_transaction(&hash, &returned_signed_message, &complete_returned_address) - .await + if Wallet::verify_transaction_with_public_key_bytes( + &hash, + &returned_signed_message, + returned_public_key_bin, + ) + .await { // At this point the handshake is complete, so the stream can carry the RPC request. request(&mut stream, json, rpc_command, hashmap_key).await diff --git a/src/standalone_tools/connections/sending_request.rs b/src/standalone_tools/connections/sending_request.rs index ff22635..d1d3c59 100644 --- a/src/standalone_tools/connections/sending_request.rs +++ b/src/standalone_tools/connections/sending_request.rs @@ -404,30 +404,35 @@ async fn build_request_bytes( bin_msg.extend_from_slice(&hashmap_key); bin_msg.extend_from_slice(&address_bytes); } - // Register a wallet using "short_address|long_address|signature". + // Register a wallet using "short_address|public_key|signature". 38 => { let command_number: u8 = RPC_REGISTER_WALLET; let (short_address, rest) = command_input.split_once('|').ok_or_else(|| { io::Error::new( io::ErrorKind::InvalidInput, - "Wallet registration input must be short|long|signature", + "Wallet registration input must be short|public_key|signature", ) })?; - let (long_address, signature) = rest.split_once('|').ok_or_else(|| { + let (public_key, signature) = rest.split_once('|').ok_or_else(|| { io::Error::new( io::ErrorKind::InvalidInput, - "Wallet registration input must be short|long|signature", + "Wallet registration input must be short|public_key|signature", ) })?; let short_address_bytes = Wallet::short_address_to_bytes(short_address).ok_or_else(|| { io::Error::new(io::ErrorKind::InvalidInput, "Invalid short wallet address") })?; - let long_address_bytes = Wallet::long_address_to_bytes(long_address.to_string()); - if long_address_bytes.len() != Wallet::ADDRESS_BYTES_LENGTH { + let public_key_bytes = decode(public_key).map_err(|err| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("Invalid wallet registration public key: {err}"), + ) + })?; + if public_key_bytes.len() != Wallet::PUBLIC_KEY_LENGTH { return Err(io::Error::new( io::ErrorKind::InvalidInput, - "Invalid long wallet address", + "Invalid wallet registration public key", )); } let signature_bytes = decode(signature).map_err(|err| { @@ -440,7 +445,7 @@ async fn build_request_bytes( bin_msg.extend_from_slice(&command_number.to_le_bytes()); bin_msg.extend_from_slice(&hashmap_key); bin_msg.extend_from_slice(&short_address_bytes); - bin_msg.extend_from_slice(&long_address_bytes); + bin_msg.extend_from_slice(&public_key_bytes); bin_msg.extend_from_slice(&signature_bytes); } // Lookup remote balance by long, short, or vanity address. diff --git a/src/standalone_tools/vanity_resolver.rs b/src/standalone_tools/vanity_resolver.rs index 4c29a58..8993ee7 100644 --- a/src/standalone_tools/vanity_resolver.rs +++ b/src/standalone_tools/vanity_resolver.rs @@ -45,7 +45,7 @@ async fn lookup_vanity_owner(vanity_address: &str, wallet: &Wallet) -> Result bytes, None => return, }; - let modified_by_bytes = vec![0u8; Wallet::ADDRESS_BYTES_LENGTH]; + let modified_by_bytes = vec![0u8; Wallet::SHORT_ADDRESS_BYTES_LENGTH]; let time = Utc::now().timestamp_millis() as u64; let modified_timestamp_bytes = time.to_le_bytes(); // Self-announcement is intentionally unsigned. The receiving node @@ -101,7 +101,7 @@ pub async fn announce_self_to_network( 1 + 3 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + 16 - + Wallet::ADDRESS_BYTES_LENGTH + + Wallet::SHORT_ADDRESS_BYTES_LENGTH + 8 + Wallet::SIGNATURE_LENGTH, ); @@ -180,7 +180,7 @@ pub async fn get_network_mapping( let added_by = if added_by_bytes.iter().all(|&byte| byte == 0) { String::new() } else { - Wallet::bytes_to_long_address(added_by_bytes.to_vec()) + Wallet::bytes_to_short_address(added_by_bytes).unwrap_or_default() }; let added_timestamp = u64::from_le_bytes( chunk[NODE_ADDED_TIMESTAMP_OFFSET..NODE_ADDED_SIGNATURE_OFFSET] diff --git a/src/wallets/create_keys.rs b/src/wallets/create_keys.rs index bbd58b4..d8cda51 100644 --- a/src/wallets/create_keys.rs +++ b/src/wallets/create_keys.rs @@ -3,7 +3,7 @@ use crate::FnDsaKeyPair; use crate::{decode, encode}; impl Wallet { - pub fn generate_keypair(network_byte: u8) -> (Vec, String) { + pub fn generate_keypair(_network_byte: u8) -> (Vec, String) { // Generate a new FN-DSA key pair using the wallet security parameter. let keypair = FnDsaKeyPair::generate(Self::FN_DSA_LOGN).expect("Failed to generate FN-DSA key pair"); @@ -11,19 +11,12 @@ impl Wallet { // Keep the private key as raw bytes until it is hex-encoded for storage. let secret_key_bytes = keypair.private_key().to_vec(); - // Keep the public key bytes separate so the network byte can be prepended. let public_key_bytes = keypair.public_key().to_vec(); - // Store long-address bytes as network byte plus the raw public key. - let mut encoded_public_key = Vec::with_capacity(Self::ADDRESS_BYTES_LENGTH); - encoded_public_key.push(network_byte); - encoded_public_key.extend_from_slice(&public_key_bytes); - // Private keys are persisted as hex before wallet-image encryption. let private_key_hex = encode(secret_key_bytes); - // Return the network-tagged public key bytes and hex private key. - (encoded_public_key, private_key_hex) + (public_key_bytes, private_key_hex) } pub fn regenerate_public_key(private_key_hex: &str) -> Result, String> { @@ -49,10 +42,7 @@ impl Wallet { let keypair = FnDsaKeyPair::from_private_key(&private_key_bytes) .map_err(|e| format!("Failed to decode the provided FN-DSA private key: {e}"))?; - // Return the public key in the same network-prefixed byte layout used by long addresses. - let mut encoded_public_key = Vec::with_capacity(Self::ADDRESS_BYTES_LENGTH); - encoded_public_key.push(network_byte); - encoded_public_key.extend_from_slice(keypair.public_key()); - Ok(encoded_public_key) + let _ = network_byte; + Ok(keypair.public_key().to_vec()) } } diff --git a/src/wallets/create_wallet.rs b/src/wallets/create_wallet.rs index f9944af..1677726 100644 --- a/src/wallets/create_wallet.rs +++ b/src/wallets/create_wallet.rs @@ -1,54 +1,17 @@ +use crate::encode; use crate::wallets::structures::SavedWallet; use crate::wallets::structures::Wallet; use crate::Path; use crate::{create_img, encrypts}; -use crate::{decode, encode}; impl Wallet { - // Derive the long wallet address from network-prefixed public key bytes. - pub fn generate_address(public_key: &str) -> String { - // Decode the hex public key into the long-address byte layout. - let public_key_bytes = decode(public_key).expect("Failed to decode public key hex"); - - // Long-address bytes must contain the network byte plus the public key. - if public_key_bytes.len() != Self::ADDRESS_BYTES_LENGTH { - panic!( - "Invalid FN-DSA public key byte count. Expected {} bytes, got {} bytes.", - Self::ADDRESS_BYTES_LENGTH, - public_key_bytes.len() - ); - } - - // The first byte selects the wallet prefix and XOR mask. - let network_byte = public_key_bytes[0]; - - // Convert the network byte back into the visible CLC/CLTC prefix. - let prefix_str = Self::map_byte_to_wallet(network_byte); - - // XOR the public key body so the visible long address is network-bound. - let xored_public_key: Vec = public_key_bytes[1..] - .iter() - .map(|&byte| byte ^ network_byte) - .collect(); - - // Return the text long address as prefix plus encoded key body. - format!("{}{}", prefix_str, encode(xored_public_key)) - } - pub fn create_wallet(&self, network_byte: u8) -> Wallet { // Generate a fresh FN-DSA key pair for the selected network. let (public_key, private_key) = Self::generate_keypair(network_byte); - // Build the visible long address from the network-prefixed public key. - let long_address = Self::generate_address(&encode(&public_key)); - - // Decode the long address so the canonical short address can be derived. - let long_address_bytes = Self::long_address_to_bytes(long_address.clone()); - // Hash the public key body into the fixed 22-byte short-address format. - let short_address_bytes = - Self::long_address_bytes_to_short_address_bytes(&long_address_bytes) - .expect("Failed to derive short address from generated wallet"); + let short_address_bytes = Self::public_key_bytes_to_short_address_bytes(&public_key) + .expect("Failed to derive short address from generated wallet"); // Convert the short-address bytes into the saved text form. let short_address = Self::bytes_to_short_address(&short_address_bytes) @@ -82,7 +45,6 @@ impl Wallet { // Build the wallet data saved to disk. let saved = SavedWallet { - long_address, short_address, vanity_address: None, public_key: encode(public_key), @@ -102,7 +64,6 @@ impl Wallet { ) -> Result { // Start with an empty saved wallet so create_wallet has an encryption key holder. let new_wallet = SavedWallet { - long_address: "".to_string(), short_address: "".to_string(), vanity_address: None, public_key: "".to_string(), diff --git a/src/wallets/short_address.rs b/src/wallets/short_address.rs index e88641a..1a4024c 100644 --- a/src/wallets/short_address.rs +++ b/src/wallets/short_address.rs @@ -14,15 +14,6 @@ impl Wallet { return Self::bytes_to_vanity_address(&vanity_address_bytes); } - // Treat anything else as a possible long wallet address. - let long_address_bytes = Self::long_address_to_bytes(address.to_string()); - if long_address_bytes.len() != Self::ADDRESS_BYTES_LENGTH { - return None; - } - - // Derive the canonical short address from valid long-address bytes. - let derived_short_address = - Self::long_address_bytes_to_short_address_bytes(&long_address_bytes)?; - Self::bytes_to_short_address(&derived_short_address) + None } } diff --git a/src/wallets/structures.rs b/src/wallets/structures.rs index 9648e87..a05163c 100644 --- a/src/wallets/structures.rs +++ b/src/wallets/structures.rs @@ -2,7 +2,6 @@ use crate::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SavedWallet { - pub long_address: String, pub short_address: String, pub vanity_address: Option, pub public_key: String, @@ -26,10 +25,7 @@ impl Wallet { pub const SHORT_ADDRESS_SEPARATOR: u8 = b'.'; pub fn display_wallet(&self) -> String { - let mut output = format!( - "Long Address: {}\nShort Address: {}", - self.saved.long_address, self.saved.short_address - ); + let mut output = format!("Short Address: {}", self.saved.short_address); if let Some(vanity_address) = &self.saved.vanity_address { output.push_str(&format!("\nVanity Address: {vanity_address}")); } diff --git a/src/wallets/verifications.rs b/src/wallets/verifications.rs index e6e5f09..723c5ec 100644 --- a/src/wallets/verifications.rs +++ b/src/wallets/verifications.rs @@ -4,17 +4,6 @@ use crate::wallets::structures::Wallet; use fn_dsa::{VerifyingKey, VerifyingKeyStandard, DOMAIN_NONE, HASH_ID_RAW}; impl Wallet { - pub async fn vrf_verify(number: u128, hash: &str, address: &str, signature: &str) -> bool { - // Derive the VRF number from the submitted signature. - let calculated_number = UnminedBlock::generate_random_number(signature).await; - - // Accept the VRF only when the signature verifies and produces the expected number. - if Self::verify_transaction(hash, signature, address).await && calculated_number == number { - return true; - } - false - } - pub async fn vrf_verify_with_public_key( number: u128, hash: &str, @@ -33,30 +22,6 @@ impl Wallet { false } - pub async fn verify_transaction(message: &str, signature_hex: &str, address: &str) -> bool { - // Decode and length-check the submitted signature. - let sig_bytes = match decode(signature_hex) { - Ok(b) if b.len() == Self::SIGNATURE_LENGTH => b, - _ => return false, - }; - - // Decode the long wallet address into network-prefixed public key bytes. - let wallet_bytes = Self::long_address_to_bytes(address.to_string()); - if wallet_bytes.len() != Self::ADDRESS_BYTES_LENGTH { - return false; - } - - // Rebuild the FN-DSA verifying key from the public key body. - let verifying_key = match VerifyingKeyStandard::decode(&wallet_bytes[1..]) { - Some(key) => key, - None => return false, - }; - - // Verify decoded hash bytes when the message is hex, otherwise verify raw message bytes. - let message_bytes = decode(message).unwrap_or_else(|_| message.as_bytes().to_vec()); - verifying_key.verify(&sig_bytes, &DOMAIN_NONE, &HASH_ID_RAW, &message_bytes) - } - pub async fn verify_transaction_with_public_key( message: &str, signature_hex: &str, @@ -84,4 +49,27 @@ impl Wallet { let message_bytes = decode(message).unwrap_or_else(|_| message.as_bytes().to_vec()); verifying_key.verify(&sig_bytes, &DOMAIN_NONE, &HASH_ID_RAW, &message_bytes) } + + pub async fn verify_transaction_with_public_key_bytes( + message: &str, + signature_hex: &str, + public_key_bytes: &[u8], + ) -> bool { + let sig_bytes = match decode(signature_hex) { + Ok(b) if b.len() == Self::SIGNATURE_LENGTH => b, + _ => return false, + }; + + if public_key_bytes.len() != Self::PUBLIC_KEY_LENGTH { + return false; + } + + let verifying_key = match VerifyingKeyStandard::decode(public_key_bytes) { + Some(key) => key, + None => return false, + }; + + let message_bytes = decode(message).unwrap_or_else(|_| message.as_bytes().to_vec()); + verifying_key.verify(&sig_bytes, &DOMAIN_NONE, &HASH_ID_RAW, &message_bytes) + } } diff --git a/src/wallets/verify_address.rs b/src/wallets/verify_address.rs index a5e151a..00df558 100644 --- a/src/wallets/verify_address.rs +++ b/src/wallets/verify_address.rs @@ -1,7 +1,7 @@ use crate::wallets::structures::Wallet; impl Wallet { - fn has_valid_public_key_bytes(public_key_bytes: &[u8]) -> bool { + pub fn has_valid_public_key_bytes(public_key_bytes: &[u8]) -> bool { // Public keys must match the FN-DSA public key byte length exactly. if public_key_bytes.len() != Self::PUBLIC_KEY_LENGTH { return false; @@ -11,41 +11,6 @@ impl Wallet { public_key_bytes[0] == Self::FN_DSA_LOGN as u8 } - // Validate a long wallet address for the active network and FN-DSA key layout. - pub async fn wallet_validation(wallet: &str) -> bool { - // Split the visible network prefix from the encoded key body. - let (network_prefix, encoded_key) = if let Some(rest) = wallet.strip_prefix("CLTC") { - ("CLTC", rest) - } else if let Some(rest) = wallet.strip_prefix("CLC") { - ("CLC", rest) - } else { - return false; - }; - - // Compare the wallet prefix against the currently compiled network. - let expected_byte = Self::current_network_byte(); - - if Wallet::map_wallet_to_byte(network_prefix) != expected_byte { - return false; - } - - // Long wallet address bodies must be the exact encoded public key length. - if encoded_key.len() != Self::ADDRESS_HEX_LENGTH { - return false; - } - - // The public key body is stored as hex text. - if !encoded_key.chars().all(|ch| ch.is_ascii_hexdigit()) { - return false; - } - - // Decode the address and verify the network byte and public key payload. - let wallet_bytes = Self::long_address_to_bytes(wallet.to_string()); - wallet_bytes.len() == Self::ADDRESS_BYTES_LENGTH - && wallet_bytes[0] == expected_byte - && Self::has_valid_public_key_bytes(&wallet_bytes[1..]) - } - pub fn short_address_validation(short_address: &str) -> bool { // Accept either canonical short-address bytes or vanity-address bytes. let short_address_bytes = match Self::short_address_to_bytes(short_address) diff --git a/src/wallets/wallet_bytes.rs b/src/wallets/wallet_bytes.rs index 5297319..bb30250 100644 --- a/src/wallets/wallet_bytes.rs +++ b/src/wallets/wallet_bytes.rs @@ -1,79 +1,8 @@ use crate::common::skein::{ripemd160_hash_bytes, skein_256_hash_bytes}; -use crate::log::error; use crate::wallets::structures::Wallet; use crate::{decode, encode}; impl Wallet { - pub fn long_address_to_bytes(wallet: String) -> Vec { - // Return an empty byte vector when the wallet cannot be decoded. - let mut wallet_bytes = Vec::new(); - - // Split the visible long-address prefix from the encoded public key body. - let (prefix, encoded_key) = if let Some(rest) = wallet.strip_prefix("CLTC") { - ("CLTC", rest) - } else if let Some(rest) = wallet.strip_prefix("CLC") { - ("CLC", rest) - } else { - return wallet_bytes; - }; - - // Long-address bodies must be exactly one public key encoded as hex. - if encoded_key.len() != Self::ADDRESS_HEX_LENGTH { - return wallet_bytes; - } - - // Convert the visible network prefix into the stored network byte. - let prefix_byte = Self::map_wallet_to_byte(prefix); - if prefix_byte == 0 { - return wallet_bytes; - } - - // Decode the XORed public key body from hex. - let xored_key_bytes = match decode(encoded_key) { - Ok(bytes) if bytes.len() == Self::PUBLIC_KEY_LENGTH => bytes, - _ => return wallet_bytes, - }; - - // Store the network byte first, followed by the unmasked public key bytes. - wallet_bytes.push(prefix_byte); - wallet_bytes.extend(xored_key_bytes.into_iter().map(|byte| byte ^ prefix_byte)); - wallet_bytes - } - - pub fn bytes_to_long_address(bytes: Vec) -> String { - // Long-address bytes must contain the network byte and public key body. - if bytes.len() != Self::ADDRESS_BYTES_LENGTH { - error!( - "Invalid byte length for wallet. Expected {} bytes, got {}", - Self::ADDRESS_BYTES_LENGTH, - bytes.len() - ); - return String::new(); - } - - // Read the network byte and public key body from the fixed layout. - let prefix_byte = bytes[0]; - let public_key_bytes = &bytes[1..Self::ADDRESS_BYTES_LENGTH]; - - // Convert the network byte into the visible long-address prefix. - let prefix = Self::map_byte_to_wallet(prefix_byte); - if prefix.is_empty() { - error!("Invalid wallet network byte: {prefix_byte}"); - return String::new(); - } - - // XOR the public key body for the visible long-address encoding. - let xored_public_key: Vec = public_key_bytes - .iter() - .map(|byte| byte ^ prefix_byte) - .collect(); - - // Encode the masked public key body as hex and prepend the network prefix. - let address = encode(xored_public_key); - let wallet = format!("{prefix}{address}"); - wallet - } - pub fn bytes_to_short_address(short_address_bytes: &[u8]) -> Option { // Short-address bytes must contain a 20-byte payload, separator, and network byte. if short_address_bytes.len() != Self::SHORT_ADDRESS_BYTES_LENGTH { @@ -185,20 +114,13 @@ impl Wallet { Some(format!("{payload}.{network_suffix}")) } - pub fn long_address_bytes_to_short_address_bytes(long_address_bytes: &[u8]) -> Option> { - // Long-address bytes must contain the network byte and public key body. - if long_address_bytes.len() != Self::ADDRESS_BYTES_LENGTH { + pub fn public_key_bytes_to_short_address_bytes(public_key_bytes: &[u8]) -> Option> { + if public_key_bytes.len() != Self::PUBLIC_KEY_LENGTH { return None; } - // Keep the source network byte as the short-address network byte. - let network_byte = long_address_bytes[0]; - if Self::map_byte_to_wallet(network_byte).is_empty() { - return None; - } - - // Hash the public key body with Skein before RIPEMD-160 shortening. - let skein_hash_hex = skein_256_hash_bytes(&long_address_bytes[1..]); + let network_byte = Self::current_network_byte(); + let skein_hash_hex = skein_256_hash_bytes(public_key_bytes); let skein_hash_bytes = decode(&skein_hash_hex).ok()?; // RIPEMD-160 produces the 20-byte short-address payload. @@ -217,6 +139,22 @@ impl Wallet { Some(short_address) } + pub fn public_key_bytes_to_short_address(public_key_bytes: &[u8]) -> Option { + let short_address_bytes = Self::public_key_bytes_to_short_address_bytes(public_key_bytes)?; + Self::bytes_to_short_address(&short_address_bytes) + } + + pub fn normalize_saved_public_key_bytes(public_key_hex: &str) -> Option> { + let bytes = decode(public_key_hex).ok()?; + if bytes.len() == Self::PUBLIC_KEY_LENGTH { + return Some(bytes); + } + if bytes.len() == Self::ADDRESS_BYTES_LENGTH && bytes[0] == Self::current_network_byte() { + return Some(bytes[1..].to_vec()); + } + None + } + pub fn sanitize_vanity_address_bytes(payload: &str) -> Option> { // Vanity payloads are stored as exactly 20 ASCII bytes. if payload.len() != Self::SHORT_ADDRESS_HASH_BYTES_LENGTH {