removed long addresses
This commit is contained in:
parent
6cd01d1345
commit
0aea32a2d5
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -33,14 +33,12 @@ fn extract_address(contents: &str) -> Result<String, String> {
|
|||
}
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ fn display_vanity_address(short_address: &str) -> Option<String> {
|
|||
|
||||
async fn lookup_registered_vanity_address(
|
||||
short_address: &str,
|
||||
long_address: &str,
|
||||
public_key: &str,
|
||||
private_key: &str,
|
||||
) -> Option<String> {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ fn display_vanity_address(short_address: &str) -> Option<String> {
|
|||
|
||||
async fn lookup_registered_vanity_address(
|
||||
short_address: &str,
|
||||
long_address: &str,
|
||||
public_key: &str,
|
||||
private_key: &str,
|
||||
) -> Option<String> {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,13 +30,12 @@ fn extract_address(contents: &str) -> Result<String, String> {
|
|||
}
|
||||
|
||||
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}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String, String> {
|
|||
}
|
||||
|
||||
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");
|
||||
|
|
|
|||
|
|
@ -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<u8>, port: u16) {
|
||||
fn spawn_monitor_update(ip: String, action: u8, peer_public_key: Vec<u8>, 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<u8>, 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();
|
||||
|
|
|
|||
|
|
@ -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,7 +110,18 @@ 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)
|
||||
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());
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
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}");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub struct ConnectionInfo {
|
|||
pub port: u16,
|
||||
pub stream: Arc<Mutex<TcpStream>>,
|
||||
pub client_type: Vec<u8>,
|
||||
pub wallet_address: Vec<u8>,
|
||||
pub wallet_public_key: Vec<u8>,
|
||||
}
|
||||
|
||||
// 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<Mutex<TcpStream>>,
|
||||
pub client_type: ClientType,
|
||||
pub wallet_address: String,
|
||||
pub wallet_public_key: Vec<u8>,
|
||||
pub command_map: Arc<Mutex<Command>>,
|
||||
}
|
||||
|
||||
|
|
@ -199,7 +199,7 @@ impl ConnectionInfo {
|
|||
port: u16,
|
||||
stream: Arc<Mutex<TcpStream>>,
|
||||
client_type: Vec<u8>,
|
||||
wallet_address: Vec<u8>,
|
||||
wallet_public_key: Vec<u8>,
|
||||
) -> Self {
|
||||
ConnectionInfo {
|
||||
connection_type,
|
||||
|
|
@ -207,7 +207,7 @@ impl ConnectionInfo {
|
|||
port,
|
||||
stream,
|
||||
client_type,
|
||||
wallet_address,
|
||||
wallet_public_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
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?;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<Mutex<Command>>,
|
||||
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<u8>,
|
||||
long_address: String,
|
||||
public_key_bytes: Vec<u8>,
|
||||
signature: String,
|
||||
db: &Db,
|
||||
map: Arc<Mutex<Command>>,
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<Mutex<TcpStream>>,
|
||||
) -> Result<String, String> {
|
||||
// Wallet addresses use a fixed binary length on the wire and are
|
||||
// decoded into their CLTC string form here.
|
||||
) -> Result<Vec<u8>, 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(
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ pub async fn write_to_memory(
|
|||
received_ip_port: &str,
|
||||
stream: Arc<Mutex<TcpStream>>,
|
||||
client_type: &str,
|
||||
wallet: &str,
|
||||
wallet_public_key: Vec<u8>,
|
||||
command_map: Arc<Mutex<Command>>,
|
||||
) -> 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,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<Mutex<TcpStream>>,
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ pub async fn verify_handshake(
|
|||
_map: Arc<Mutex<Command>>,
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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<Vec<u8>, 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<Vec<u8>, 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,23 +173,26 @@ 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)
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ async fn lookup_vanity_owner(vanity_address: &str, wallet: &Wallet) -> Result<St
|
|||
vanity_address.to_string(),
|
||||
RPC_VANITY_OWNER_LOOKUP as usize,
|
||||
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(),
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ async fn record_local_monitor_for_peer(
|
|||
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_wallet_bytes)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let monitoring_address = wallet.saved.short_address.clone();
|
||||
|
|
@ -90,7 +90,7 @@ pub async fn announce_self_to_network(
|
|||
Some(bytes) => 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]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::FnDsaKeyPair;
|
|||
use crate::{decode, encode};
|
||||
|
||||
impl Wallet {
|
||||
pub fn generate_keypair(network_byte: u8) -> (Vec<u8>, String) {
|
||||
pub fn generate_keypair(_network_byte: u8) -> (Vec<u8>, 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<Vec<u8>, 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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,53 +1,16 @@
|
|||
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<u8> = 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)
|
||||
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.
|
||||
|
|
@ -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<Wallet, String> {
|
||||
// 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(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
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}"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<u8> {
|
||||
// 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<u8>) -> 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<u8> = 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<String> {
|
||||
// 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<Vec<u8>> {
|
||||
// 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<Vec<u8>> {
|
||||
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<String> {
|
||||
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<Vec<u8>> {
|
||||
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<Vec<u8>> {
|
||||
// Vanity payloads are stored as exactly 20 ASCII bytes.
|
||||
if payload.len() != Self::SHORT_ADDRESS_HASH_BYTES_LENGTH {
|
||||
|
|
|
|||
Loading…
Reference in New Issue