removed long addresses
This commit is contained in:
parent
6cd01d1345
commit
0aea32a2d5
|
|
@ -63,7 +63,7 @@ async fn main() {
|
||||||
wallet_address.clone(),
|
wallet_address.clone(),
|
||||||
rpc_command,
|
rpc_command,
|
||||||
handshake::HandshakeWallet::WalletParts {
|
handshake::HandshakeWallet::WalletParts {
|
||||||
long_address: wallet.saved.long_address.clone(),
|
public_key: wallet.saved.public_key.clone(),
|
||||||
private_key: wallet.saved.private_key.clone(),
|
private_key: wallet.saved.private_key.clone(),
|
||||||
},
|
},
|
||||||
hashmap_key,
|
hashmap_key,
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ async fn main() {
|
||||||
address.clone(),
|
address.clone(),
|
||||||
rpc_command,
|
rpc_command,
|
||||||
handshake::HandshakeWallet::WalletParts {
|
handshake::HandshakeWallet::WalletParts {
|
||||||
long_address: wallet.saved.long_address.clone(),
|
public_key: wallet.saved.public_key.clone(),
|
||||||
private_key: wallet.saved.private_key.clone(),
|
private_key: wallet.saved.private_key.clone(),
|
||||||
},
|
},
|
||||||
generate_uid(),
|
generate_uid(),
|
||||||
|
|
|
||||||
|
|
@ -33,14 +33,12 @@ fn extract_address(contents: &str) -> Result<String, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if trimmed.starts_with('{') {
|
if trimmed.starts_with('{') {
|
||||||
// Prefer short_address for balance lookups, but accept long_address too.
|
|
||||||
let value: Value =
|
let value: Value =
|
||||||
from_str(trimmed).map_err(|e| format!("Failed to parse wallet JSON: {e}"))?;
|
from_str(trimmed).map_err(|e| format!("Failed to parse wallet JSON: {e}"))?;
|
||||||
let address = value
|
let address = value
|
||||||
.get("short_address")
|
.get("short_address")
|
||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
.or_else(|| value.get("vanity_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())?;
|
.ok_or_else(|| "Wallet JSON does not contain a usable address field".to_string())?;
|
||||||
return Ok(address.trim().to_string());
|
return Ok(address.trim().to_string());
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +147,7 @@ async fn main() {
|
||||||
json.clone(),
|
json.clone(),
|
||||||
rpc_command,
|
rpc_command,
|
||||||
handshake::HandshakeWallet::WalletParts {
|
handshake::HandshakeWallet::WalletParts {
|
||||||
long_address: wallet.saved.long_address.clone(),
|
public_key: wallet.saved.public_key.clone(),
|
||||||
private_key: wallet.saved.private_key.clone(),
|
private_key: wallet.saved.private_key.clone(),
|
||||||
},
|
},
|
||||||
hashmap_key,
|
hashmap_key,
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ fn display_vanity_address(short_address: &str) -> Option<String> {
|
||||||
|
|
||||||
async fn lookup_registered_vanity_address(
|
async fn lookup_registered_vanity_address(
|
||||||
short_address: &str,
|
short_address: &str,
|
||||||
long_address: &str,
|
public_key: &str,
|
||||||
private_key: &str,
|
private_key: &str,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let hashmap_key = generate_uid();
|
let hashmap_key = generate_uid();
|
||||||
|
|
@ -53,7 +53,7 @@ async fn lookup_registered_vanity_address(
|
||||||
short_address.to_string(),
|
short_address.to_string(),
|
||||||
rpc_command,
|
rpc_command,
|
||||||
handshake::HandshakeWallet::WalletParts {
|
handshake::HandshakeWallet::WalletParts {
|
||||||
long_address: long_address.to_string(),
|
public_key: public_key.to_string(),
|
||||||
private_key: private_key.to_string(),
|
private_key: private_key.to_string(),
|
||||||
},
|
},
|
||||||
hashmap_key,
|
hashmap_key,
|
||||||
|
|
@ -209,17 +209,14 @@ async fn main() {
|
||||||
match Wallet::regenerate_public_key(&private_key) {
|
match Wallet::regenerate_public_key(&private_key) {
|
||||||
Ok(public_key_bytes) => {
|
Ok(public_key_bytes) => {
|
||||||
let public_key = blockchain::encode(public_key_bytes.clone());
|
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 =
|
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");
|
.expect("Failed to derive short address bytes");
|
||||||
let short_address = Wallet::bytes_to_short_address(&short_address_bytes)
|
let short_address = Wallet::bytes_to_short_address(&short_address_bytes)
|
||||||
.expect("Failed to encode short address");
|
.expect("Failed to encode short address");
|
||||||
let vanity_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 {
|
let saved_wallet = SavedWallet {
|
||||||
long_address,
|
|
||||||
short_address,
|
short_address,
|
||||||
vanity_address,
|
vanity_address,
|
||||||
public_key,
|
public_key,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ fn display_vanity_address(short_address: &str) -> Option<String> {
|
||||||
|
|
||||||
async fn lookup_registered_vanity_address(
|
async fn lookup_registered_vanity_address(
|
||||||
short_address: &str,
|
short_address: &str,
|
||||||
long_address: &str,
|
public_key: &str,
|
||||||
private_key: &str,
|
private_key: &str,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let hashmap_key = generate_uid();
|
let hashmap_key = generate_uid();
|
||||||
|
|
@ -51,7 +51,7 @@ async fn lookup_registered_vanity_address(
|
||||||
short_address.to_string(),
|
short_address.to_string(),
|
||||||
rpc_command,
|
rpc_command,
|
||||||
handshake::HandshakeWallet::WalletParts {
|
handshake::HandshakeWallet::WalletParts {
|
||||||
long_address: long_address.to_string(),
|
public_key: public_key.to_string(),
|
||||||
private_key: private_key.to_string(),
|
private_key: private_key.to_string(),
|
||||||
},
|
},
|
||||||
hashmap_key,
|
hashmap_key,
|
||||||
|
|
@ -246,17 +246,14 @@ async fn main() {
|
||||||
match Wallet::regenerate_public_key(&private_key) {
|
match Wallet::regenerate_public_key(&private_key) {
|
||||||
Ok(public_key_bytes) => {
|
Ok(public_key_bytes) => {
|
||||||
let public_key = blockchain::encode(public_key_bytes.clone());
|
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 =
|
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");
|
.expect("Failed to derive short address bytes");
|
||||||
let short_address = Wallet::bytes_to_short_address(&short_address_bytes)
|
let short_address = Wallet::bytes_to_short_address(&short_address_bytes)
|
||||||
.expect("Failed to encode short address");
|
.expect("Failed to encode short address");
|
||||||
let vanity_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 {
|
let saved_wallet = SavedWallet {
|
||||||
long_address,
|
|
||||||
short_address,
|
short_address,
|
||||||
vanity_address,
|
vanity_address,
|
||||||
public_key,
|
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 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) {
|
let short_address_bytes = match Wallet::short_address_to_bytes(&short_address) {
|
||||||
Some(bytes) => bytes,
|
Some(bytes) => bytes,
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -45,20 +44,28 @@ async fn main() {
|
||||||
return;
|
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 =
|
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.push(RPC_REGISTER_WALLET);
|
||||||
signed_payload.extend_from_slice(&short_address_bytes);
|
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 payload_hash = skein_256_hash_bytes(&signed_payload);
|
||||||
let signature = Wallet::sign_transaction(&payload_hash, &wallet.saved.private_key).await;
|
let signature = Wallet::sign_transaction(&payload_hash, &wallet.saved.private_key).await;
|
||||||
|
|
||||||
// sending_request encodes command 38 from this pipe-delimited payload.
|
// 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 rpc_command = 38;
|
||||||
let connections = get_connections().await;
|
let connections = get_connections().await;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,12 @@ fn extract_address(contents: &str) -> Result<String, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if trimmed.starts_with('{') {
|
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 =
|
let value: Value =
|
||||||
from_str(trimmed).map_err(|e| format!("Failed to parse wallet JSON: {e}"))?;
|
from_str(trimmed).map_err(|e| format!("Failed to parse wallet JSON: {e}"))?;
|
||||||
let address = value
|
let address = value
|
||||||
.get("short_address")
|
.get("short_address")
|
||||||
.and_then(|v| v.as_str())
|
.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())?;
|
.ok_or_else(|| "Wallet JSON does not contain a usable address field".to_string())?;
|
||||||
return Ok(address.trim().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::short_address_validation(address.trim());
|
||||||
let message_hash = Wallet::wallet_validation(address.trim()).await
|
|
||||||
|| Wallet::short_address_validation(address.trim());
|
|
||||||
|
|
||||||
println!("{message_hash}");
|
println!("{message_hash}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
use blockchain::common::cli_prompts::prompt_visible;
|
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::common::skein::skein_256_hash_data;
|
||||||
use blockchain::env;
|
use blockchain::env;
|
||||||
use blockchain::from_str;
|
use blockchain::from_str;
|
||||||
use blockchain::read_to_string;
|
use blockchain::read_to_string;
|
||||||
|
use blockchain::records::wallet_registry::resolve_pubkey_from_short_address;
|
||||||
|
use blockchain::sled;
|
||||||
use blockchain::tilde;
|
use blockchain::tilde;
|
||||||
use blockchain::wallets::structures::Wallet;
|
use blockchain::wallets::structures::Wallet;
|
||||||
use blockchain::Value;
|
use blockchain::Value;
|
||||||
|
|
@ -41,13 +44,12 @@ fn extract_address(contents: &str) -> Result<String, String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if trimmed.starts_with('{') {
|
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 =
|
let value: Value =
|
||||||
from_str(trimmed).map_err(|e| format!("Failed to parse wallet JSON: {e}"))?;
|
from_str(trimmed).map_err(|e| format!("Failed to parse wallet JSON: {e}"))?;
|
||||||
let address = value
|
let address = value
|
||||||
.get("long_address")
|
.get("short_address")
|
||||||
.and_then(|v| v.as_str())
|
.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())?;
|
.ok_or_else(|| "Wallet JSON does not contain a usable address field".to_string())?;
|
||||||
return Ok(address.trim().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.
|
// Hash the message exactly as sign_message does before verifying the wallet signature.
|
||||||
let message_hash = skein_256_hash_data(message.as_str());
|
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 {
|
if signature {
|
||||||
println!("valid signature");
|
println!("valid signature");
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ impl Connection {
|
||||||
port,
|
port,
|
||||||
stream,
|
stream,
|
||||||
client_type,
|
client_type,
|
||||||
wallet_address,
|
wallet_public_key,
|
||||||
command_map,
|
command_map,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
|
|
@ -250,8 +250,7 @@ impl Connection {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let address = Wallet::long_address_to_bytes(wallet_address);
|
if wallet_public_key.len() != Wallet::PUBLIC_KEY_LENGTH {
|
||||||
if address.len() != Wallet::ADDRESS_BYTES_LENGTH {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let connection_info = ConnectionInfo::new(
|
let connection_info = ConnectionInfo::new(
|
||||||
|
|
@ -260,12 +259,17 @@ impl Connection {
|
||||||
port,
|
port,
|
||||||
stream.clone(),
|
stream.clone(),
|
||||||
client_type.as_bytes(),
|
client_type.as_bytes(),
|
||||||
address.clone(),
|
wallet_public_key.clone(),
|
||||||
);
|
);
|
||||||
self.connection_map.insert(connection_key, connection_info);
|
self.connection_map.insert(connection_key, connection_info);
|
||||||
|
|
||||||
if client_type == ClientType::Miner {
|
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);
|
Connection::client_checkup(stream, connection_type, ip, port, command_map);
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|
@ -290,7 +294,7 @@ impl Connection {
|
||||||
spawn_monitor_update(
|
spawn_monitor_update(
|
||||||
ip.clone(),
|
ip.clone(),
|
||||||
MONITOR_ACTION_REMOVE,
|
MONITOR_ACTION_REMOVE,
|
||||||
connection_info.wallet_address.clone(),
|
connection_info.wallet_public_key.clone(),
|
||||||
port,
|
port,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -509,7 +513,7 @@ impl Connection {
|
||||||
&& connection_key.port == port
|
&& connection_key.port == port
|
||||||
&& ClientType::from_bytes(&info.client_type) == Some(ClientType::Miner)
|
&& ClientType::from_bytes(&info.client_type) == Some(ClientType::Miner)
|
||||||
{
|
{
|
||||||
Some(info.wallet_address.clone())
|
Some(info.wallet_public_key.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
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 {
|
tokio::spawn(async move {
|
||||||
let context = {
|
let context = {
|
||||||
let guard = RECONNECT_CONTEXT.lock().await;
|
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 {
|
let Some(context) = context else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let peer_long = Wallet::bytes_to_long_address(peer_wallet_bytes);
|
let Some(monitored_address) = Wallet::public_key_bytes_to_short_address(&peer_public_key)
|
||||||
let Some(monitored_address) = Wallet::normalize_to_short_address(&peer_long) else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let monitoring_address = context.wallet.saved.short_address.clone();
|
let monitoring_address = context.wallet.saved.short_address.clone();
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,10 @@ impl NodeInfo {
|
||||||
return;
|
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_timestamp_bytes = edit.modified_timestamp.to_le_bytes();
|
||||||
let modified_signature_bytes = decode(&edit.modified_signature).unwrap();
|
let modified_signature_bytes = decode(&edit.modified_signature).unwrap();
|
||||||
let connections_lock = CONNECTIONS.read().await;
|
let connections_lock = CONNECTIONS.read().await;
|
||||||
|
|
@ -90,7 +93,7 @@ impl NodeInfo {
|
||||||
// current timestamp so they can be propagated as fresh node events.
|
// current timestamp so they can be propagated as fresh node events.
|
||||||
if edit.ip == remote_ip {
|
if edit.ip == remote_ip {
|
||||||
edit.modified_timestamp = current_timestamp;
|
edit.modified_timestamp = current_timestamp;
|
||||||
edit.modified_by = wallet.saved.long_address.clone();
|
edit.modified_by = wallet.saved.short_address.clone();
|
||||||
edit.modified_signature =
|
edit.modified_signature =
|
||||||
Self::added_signature(&edit.address, &edit.ip, current_timestamp, &wallet).await;
|
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
|
// Every add/delete edit is signed, so the network map accepts
|
||||||
// only node changes backed by a valid wallet signature.
|
// 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(
|
||||||
.await
|
&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());
|
return RpcResponse::Binary(b"Error: Could not validate signature".to_vec());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,6 @@ impl NodeInfo {
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let signer = Wallet::bytes_to_long_address(pubkey);
|
|
||||||
let data = format!(
|
let data = format!(
|
||||||
"{}{}{}{}{}",
|
"{}{}{}{}{}",
|
||||||
edit.action,
|
edit.action,
|
||||||
|
|
@ -139,7 +138,12 @@ impl NodeInfo {
|
||||||
edit.modified_timestamp
|
edit.modified_timestamp
|
||||||
);
|
);
|
||||||
let hashed_data = skein_256_hash_data(&data);
|
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 {
|
pub async fn add_monitor(params: MonitorAddressParams) -> RpcResponse {
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ impl NodeInfo {
|
||||||
};
|
};
|
||||||
let ip_bytes = ip_to_binary(&node_info.ip);
|
let ip_bytes = ip_to_binary(&node_info.ip);
|
||||||
let blocks_mined = node_info.blocks_mined;
|
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 added_timestamp_bytes = node_info.added_timestamp.to_le_bytes();
|
||||||
|
|
||||||
let deleted_timestamp_bytes = node_info.deleted_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(&address_bytes);
|
||||||
data.extend_from_slice(&ip_bytes);
|
data.extend_from_slice(&ip_bytes);
|
||||||
data.push(blocks_mined);
|
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_timestamp_bytes);
|
||||||
data.extend_from_slice(&added_signature_bytes);
|
data.extend_from_slice(&added_signature_bytes);
|
||||||
data.extend_from_slice(&deleted_timestamp_bytes);
|
data.extend_from_slice(&deleted_timestamp_bytes);
|
||||||
|
|
@ -107,7 +112,7 @@ impl NodeInfo {
|
||||||
) -> String {
|
) -> String {
|
||||||
// Node edits are signed over address, IP, signer, and timestamp
|
// Node edits are signed over address, IP, signer, and timestamp
|
||||||
// so peers can independently verify the advertised change.
|
// 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 private_key = &wallet.saved.private_key;
|
||||||
|
|
||||||
let data = format!("{address}{ip}{added_by}{current_timestamp}");
|
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_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_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_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_ADDED_SIGNATURE_OFFSET: usize = NODE_ADDED_TIMESTAMP_OFFSET + NODE_TIMESTAMP_BYTES;
|
||||||
pub const NODE_DELETED_TIMESTAMP_OFFSET: usize =
|
pub const NODE_DELETED_TIMESTAMP_OFFSET: usize =
|
||||||
NODE_ADDED_SIGNATURE_OFFSET + Wallet::SIGNATURE_LENGTH;
|
NODE_ADDED_SIGNATURE_OFFSET + Wallet::SIGNATURE_LENGTH;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ pub struct ConnectionInfo {
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub stream: Arc<Mutex<TcpStream>>,
|
pub stream: Arc<Mutex<TcpStream>>,
|
||||||
pub client_type: Vec<u8>,
|
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.
|
// ConnectionKey is the stable lookup key used inside the in-memory connection map.
|
||||||
|
|
@ -34,7 +34,7 @@ pub struct StoreConnectionParams {
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub stream: Arc<Mutex<TcpStream>>,
|
pub stream: Arc<Mutex<TcpStream>>,
|
||||||
pub client_type: ClientType,
|
pub client_type: ClientType,
|
||||||
pub wallet_address: String,
|
pub wallet_public_key: Vec<u8>,
|
||||||
pub command_map: Arc<Mutex<Command>>,
|
pub command_map: Arc<Mutex<Command>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,7 +199,7 @@ impl ConnectionInfo {
|
||||||
port: u16,
|
port: u16,
|
||||||
stream: Arc<Mutex<TcpStream>>,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
client_type: Vec<u8>,
|
client_type: Vec<u8>,
|
||||||
wallet_address: Vec<u8>,
|
wallet_public_key: Vec<u8>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ConnectionInfo {
|
ConnectionInfo {
|
||||||
connection_type,
|
connection_type,
|
||||||
|
|
@ -207,7 +207,7 @@ impl ConnectionInfo {
|
||||||
port,
|
port,
|
||||||
stream,
|
stream,
|
||||||
client_type,
|
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 =
|
let message_bin =
|
||||||
decode(message).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
|
decode(message).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
|
||||||
let signed_bin =
|
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);
|
let mut data = Vec::with_capacity(HANDSHAKE_REQUEST_BYTES);
|
||||||
data.extend_from_slice(&message_bin);
|
data.extend_from_slice(&message_bin);
|
||||||
data.extend_from_slice(&signed_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(&time_bin);
|
||||||
data.extend_from_slice(&ip);
|
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_message_bin = &response[..HANDSHAKE_MESSAGE_BYTES];
|
||||||
let returned_signed_bin = &response[HANDSHAKE_SIGNATURE_OFFSET..HANDSHAKE_ADDRESS_OFFSET];
|
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_message = encode(returned_message_bin);
|
||||||
let returned_signed_message = encode(returned_signed_bin);
|
let returned_signed_message = encode(returned_signed_bin);
|
||||||
if Wallet::map_byte_to_wallet(returned_address_bin[0]).is_empty() {
|
if returned_public_key_bin.len() != Wallet::PUBLIC_KEY_LENGTH {
|
||||||
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 {
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let hash = skein_256_hash_data(&returned_message);
|
let hash = skein_256_hash_data(&returned_message);
|
||||||
|
|
||||||
let valid_response_signature =
|
let valid_response_signature = Wallet::verify_transaction_with_public_key_bytes(
|
||||||
Wallet::verify_transaction(&hash, &returned_signed_message, &returned_address).await;
|
&hash,
|
||||||
|
&returned_signed_message,
|
||||||
|
returned_public_key_bin,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
if returned_message == "ecaf" && valid_response_signature {
|
if returned_message == "ecaf" && valid_response_signature {
|
||||||
return Err(io::Error::other(
|
return Err(io::Error::other(
|
||||||
|
|
@ -290,7 +286,7 @@ pub async fn process_handshake_response(
|
||||||
port,
|
port,
|
||||||
stream: Arc::clone(&stream),
|
stream: Arc::clone(&stream),
|
||||||
client_type: ClientType::Miner,
|
client_type: ClientType::Miner,
|
||||||
wallet_address: returned_address.clone(),
|
wallet_public_key: returned_public_key_bin.to_vec(),
|
||||||
command_map: params.map.clone(),
|
command_map: params.map.clone(),
|
||||||
}) {
|
}) {
|
||||||
return Err(io::Error::other(
|
return Err(io::Error::other(
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,14 @@ pub async fn register_connected_wallet(
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let short_address_bytes = Wallet::short_address_to_bytes(&wallet.saved.short_address)
|
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())?;
|
.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());
|
let public_key_bytes = Wallet::normalize_saved_public_key_bytes(&wallet.saved.public_key)
|
||||||
if long_address_bytes.len() != Wallet::ADDRESS_BYTES_LENGTH {
|
.ok_or_else(|| "Startup wallet public key was invalid".to_string())?;
|
||||||
return Err("Startup wallet long address was invalid".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut signed_payload =
|
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.push(RPC_REGISTER_WALLET);
|
||||||
signed_payload.extend_from_slice(&short_address_bytes);
|
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 payload_hash = skein_256_hash_bytes(&signed_payload);
|
||||||
let signature = Wallet::sign_transaction(&payload_hash, &wallet.saved.private_key).await;
|
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(
|
let mut message = Vec::with_capacity(
|
||||||
1 + 3
|
1 + 3
|
||||||
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
|
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
|
||||||
+ Wallet::ADDRESS_BYTES_LENGTH
|
+ Wallet::PUBLIC_KEY_LENGTH
|
||||||
+ Wallet::SIGNATURE_LENGTH,
|
+ Wallet::SIGNATURE_LENGTH,
|
||||||
);
|
);
|
||||||
message.push(RPC_REGISTER_WALLET);
|
message.push(RPC_REGISTER_WALLET);
|
||||||
message.extend_from_slice(&hashmap_key);
|
message.extend_from_slice(&hashmap_key);
|
||||||
message.extend_from_slice(&short_address_bytes);
|
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);
|
message.extend_from_slice(&signature_bytes);
|
||||||
|
|
||||||
RpcResponse::send_raw(&stream, Some(&connections_key), &message).await;
|
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?;
|
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(
|
let added_by_bytes = read_bytes_from_stream::read_usize_from_stream(
|
||||||
connections_key,
|
connections_key,
|
||||||
Wallet::ADDRESS_BYTES_LENGTH,
|
Wallet::SHORT_ADDRESS_BYTES_LENGTH,
|
||||||
stream_locked.clone(),
|
stream_locked.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let added_by = if added_by_bytes.iter().all(|&byte| byte == 0) {
|
let added_by = if added_by_bytes.iter().all(|&byte| byte == 0) {
|
||||||
String::new()
|
String::new()
|
||||||
} else {
|
} else {
|
||||||
Wallet::bytes_to_long_address(added_by_bytes)
|
Wallet::bytes_to_short_address(&added_by_bytes).unwrap_or_default()
|
||||||
};
|
};
|
||||||
let added_timestamp =
|
let added_timestamp =
|
||||||
read_bytes_from_stream::read_u64_from_stream(connections_key, stream_locked.clone())
|
read_bytes_from_stream::read_u64_from_stream(connections_key, stream_locked.clone())
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ pub async fn block_peer(
|
||||||
) -> RpcResponse {
|
) -> RpcResponse {
|
||||||
// Peer blocking is restricted to the local node owner, proven by a
|
// Peer blocking is restricted to the local node owner, proven by a
|
||||||
// signature from the locally loaded wallet over the target IP string.
|
// 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 tree = db.open_tree("blocked_peers").unwrap();
|
||||||
let key = ip.clone();
|
let key = ip.clone();
|
||||||
let value = b"blocked";
|
let value = b"blocked";
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,13 @@ pub async fn delete_network_node(
|
||||||
.ok_or_else(|| "error: Invalid short address bytes".to_string())?;
|
.ok_or_else(|| "error: Invalid short address bytes".to_string())?;
|
||||||
let ip =
|
let ip =
|
||||||
read_bytes_from_stream::read_ip_from_stream(connections_key, stream_locked.clone()).await?;
|
read_bytes_from_stream::read_ip_from_stream(connections_key, stream_locked.clone()).await?;
|
||||||
let deleted_by =
|
let deleted_by_bytes = read_bytes_from_stream::read_short_address_from_stream(
|
||||||
read_bytes_from_stream::read_wallet_from_stream(connections_key, stream_locked.clone())
|
connections_key,
|
||||||
.await?;
|
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 =
|
let deleted_timestamp =
|
||||||
read_bytes_from_stream::read_u64_from_stream(connections_key, stream_locked.clone())
|
read_bytes_from_stream::read_u64_from_stream(connections_key, stream_locked.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ pub async fn unblock_peer(
|
||||||
) -> RpcResponse {
|
) -> RpcResponse {
|
||||||
// Peer unblocking is restricted to the local node owner, proven by a
|
// Peer unblocking is restricted to the local node owner, proven by a
|
||||||
// signature from the locally loaded wallet over the target IP string.
|
// 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 tree = db.open_tree("blocked_peers").unwrap();
|
||||||
let key = ip.clone();
|
let key = ip.clone();
|
||||||
let _ = tree.remove(key);
|
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::rpc::responses::RpcResponse;
|
||||||
|
use crate::sled::Db;
|
||||||
use crate::wallets::structures::Wallet;
|
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
|
// Signed-message verification returns a simple text status for
|
||||||
// lightweight external clients.
|
// 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();
|
let msg = "valid".to_string().as_bytes().to_vec();
|
||||||
RpcResponse::Binary(msg)
|
RpcResponse::Binary(msg)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use crate::Mutex;
|
||||||
|
|
||||||
async fn broadcast_wallet_registration(
|
async fn broadcast_wallet_registration(
|
||||||
short_address: &[u8],
|
short_address: &[u8],
|
||||||
long_address_bytes: &[u8],
|
public_key_bytes: &[u8],
|
||||||
signature: &str,
|
signature: &str,
|
||||||
map: Arc<Mutex<Command>>,
|
map: Arc<Mutex<Command>>,
|
||||||
remote_ip: &str,
|
remote_ip: &str,
|
||||||
|
|
@ -54,14 +54,14 @@ async fn broadcast_wallet_registration(
|
||||||
let mut message = Vec::with_capacity(
|
let mut message = Vec::with_capacity(
|
||||||
1 + 3
|
1 + 3
|
||||||
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
|
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
|
||||||
+ Wallet::ADDRESS_BYTES_LENGTH
|
+ Wallet::PUBLIC_KEY_LENGTH
|
||||||
+ Wallet::SIGNATURE_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.push(RPC_REGISTER_WALLET);
|
||||||
message.extend_from_slice(&hashmap_key);
|
message.extend_from_slice(&hashmap_key);
|
||||||
message.extend_from_slice(short_address);
|
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);
|
message.extend_from_slice(&signature_bytes);
|
||||||
|
|
||||||
RpcResponse::send_raw(&unlocked_stream, Some(connections_key), &message).await;
|
RpcResponse::send_raw(&unlocked_stream, Some(connections_key), &message).await;
|
||||||
|
|
@ -79,7 +79,7 @@ async fn broadcast_wallet_registration(
|
||||||
|
|
||||||
pub async fn register(
|
pub async fn register(
|
||||||
short_address_bytes: Vec<u8>,
|
short_address_bytes: Vec<u8>,
|
||||||
long_address: String,
|
public_key_bytes: Vec<u8>,
|
||||||
signature: String,
|
signature: String,
|
||||||
db: &Db,
|
db: &Db,
|
||||||
map: Arc<Mutex<Command>>,
|
map: Arc<Mutex<Command>>,
|
||||||
|
|
@ -97,17 +97,12 @@ pub async fn register(
|
||||||
return RpcResponse::Binary(b"0".to_vec());
|
return RpcResponse::Binary(b"0".to_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !Wallet::wallet_validation(&long_address).await {
|
if public_key_bytes.len() != Wallet::PUBLIC_KEY_LENGTH {
|
||||||
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 {
|
|
||||||
return RpcResponse::Binary(b"0".to_vec());
|
return RpcResponse::Binary(b"0".to_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected_short_address =
|
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,
|
Some(address) => address,
|
||||||
None => return RpcResponse::Binary(b"0".to_vec()),
|
None => return RpcResponse::Binary(b"0".to_vec()),
|
||||||
};
|
};
|
||||||
|
|
@ -119,27 +114,29 @@ pub async fn register(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut signed_payload =
|
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.push(RPC_REGISTER_WALLET);
|
||||||
signed_payload.extend_from_slice(&short_address_bytes);
|
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
|
// Registrations are signed over the exact command payload so peers
|
||||||
// can verify rebroadcasted registrations without trusting the sender.
|
// can verify rebroadcasted registrations without trusting the sender.
|
||||||
let payload_hash = skein_256_hash_bytes(&signed_payload);
|
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());
|
return RpcResponse::Binary(b"0".to_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
let public_key_bytes = &long_address_bytes[1..];
|
match register_short_address(db, &short_address_bytes, &public_key_bytes) {
|
||||||
|
|
||||||
// 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) {
|
|
||||||
Ok(WalletRegistrationResult::Inserted) => {
|
Ok(WalletRegistrationResult::Inserted) => {
|
||||||
broadcast_wallet_registration(
|
broadcast_wallet_registration(
|
||||||
&short_address_bytes,
|
&short_address_bytes,
|
||||||
&long_address_bytes,
|
&public_key_bytes,
|
||||||
&signature,
|
&signature,
|
||||||
map,
|
map,
|
||||||
&remote_ip,
|
&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_SIGNATURE_OFFSET: usize = HANDSHAKE_MESSAGE_BYTES;
|
||||||
pub const HANDSHAKE_ADDRESS_OFFSET: usize = HANDSHAKE_SIGNATURE_OFFSET + Wallet::SIGNATURE_LENGTH;
|
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_IP_OFFSET: usize = HANDSHAKE_TIME_OFFSET + HANDSHAKE_TIME_BYTES;
|
||||||
|
|
||||||
pub const HANDSHAKE_REQUEST_BYTES: usize = HANDSHAKE_IP_OFFSET + HANDSHAKE_IP_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)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_wallet_from_stream(
|
pub async fn read_public_key_from_stream(
|
||||||
key: &str,
|
key: &str,
|
||||||
stream_locked: Arc<Mutex<TcpStream>>,
|
stream_locked: Arc<Mutex<TcpStream>>,
|
||||||
) -> Result<String, String> {
|
) -> Result<Vec<u8>, String> {
|
||||||
// Wallet addresses use a fixed binary length on the wire and are
|
|
||||||
// decoded into their CLTC string form here.
|
|
||||||
let mut stream = stream_locked.lock().await;
|
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?;
|
read_exact_from_stream(key, &mut stream, &mut buffer).await?;
|
||||||
drop(stream);
|
drop(stream);
|
||||||
let address = Wallet::bytes_to_long_address(buffer.to_vec());
|
Ok(buffer)
|
||||||
Ok(address)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_short_address_from_stream(
|
pub async fn read_short_address_from_stream(
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ pub async fn write_to_memory(
|
||||||
received_ip_port: &str,
|
received_ip_port: &str,
|
||||||
stream: Arc<Mutex<TcpStream>>,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
client_type: &str,
|
client_type: &str,
|
||||||
wallet: &str,
|
wallet_public_key: Vec<u8>,
|
||||||
command_map: Arc<Mutex<Command>>,
|
command_map: Arc<Mutex<Command>>,
|
||||||
) -> String {
|
) -> String {
|
||||||
// Reject unknown connection labels before the connection manager is
|
// Reject unknown connection labels before the connection manager is
|
||||||
|
|
@ -52,7 +52,7 @@ pub async fn write_to_memory(
|
||||||
port,
|
port,
|
||||||
stream: stream.clone(),
|
stream: stream.clone(),
|
||||||
client_type,
|
client_type,
|
||||||
wallet_address: wallet.to_string(),
|
wallet_public_key,
|
||||||
command_map,
|
command_map,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ pub async fn handle_handshake(
|
||||||
let Ok((
|
let Ok((
|
||||||
received_message,
|
received_message,
|
||||||
received_signed_message,
|
received_signed_message,
|
||||||
received_address,
|
received_public_key,
|
||||||
hash,
|
hash,
|
||||||
received_ip,
|
received_ip,
|
||||||
peer_time,
|
peer_time,
|
||||||
|
|
@ -116,7 +116,7 @@ pub async fn handle_handshake(
|
||||||
incoming_connections,
|
incoming_connections,
|
||||||
hash: &hash,
|
hash: &hash,
|
||||||
received_signed_message: &received_signed_message,
|
received_signed_message: &received_signed_message,
|
||||||
received_address: &received_address,
|
received_address: &received_public_key,
|
||||||
received_ip: &received_ip,
|
received_ip: &received_ip,
|
||||||
})
|
})
|
||||||
.await
|
.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
|
// write to memory
|
||||||
let connections_key = write_to_memory(
|
let connections_key = write_to_memory(
|
||||||
&received_ip,
|
&received_ip,
|
||||||
stream.clone(),
|
stream.clone(),
|
||||||
connection_type,
|
connection_type,
|
||||||
&received_address,
|
received_public_key_bytes,
|
||||||
map.clone(),
|
map.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -202,7 +210,7 @@ pub async fn handle_handshake(
|
||||||
connection_type: connection_type.to_string(),
|
connection_type: connection_type.to_string(),
|
||||||
wallet: wallet.clone(),
|
wallet: wallet.clone(),
|
||||||
map,
|
map,
|
||||||
returned_address: received_address.clone(),
|
returned_address: received_public_key.clone(),
|
||||||
};
|
};
|
||||||
if combine_and_send_data(params).await.is_ok() && is_miner {
|
if combine_and_send_data(params).await.is_ok() && is_miner {
|
||||||
complete_incoming_miner_setup(
|
complete_incoming_miner_setup(
|
||||||
|
|
|
||||||
|
|
@ -48,14 +48,7 @@ pub async fn parse_received_data(
|
||||||
let received_message = encode(&buffer[..HANDSHAKE_MESSAGE_BYTES]);
|
let received_message = encode(&buffer[..HANDSHAKE_MESSAGE_BYTES]);
|
||||||
let received_signed_message =
|
let received_signed_message =
|
||||||
encode(&buffer[HANDSHAKE_SIGNATURE_OFFSET..HANDSHAKE_ADDRESS_OFFSET]);
|
encode(&buffer[HANDSHAKE_SIGNATURE_OFFSET..HANDSHAKE_ADDRESS_OFFSET]);
|
||||||
let received_address_bytes = &buffer[HANDSHAKE_ADDRESS_OFFSET..HANDSHAKE_TIME_OFFSET];
|
let received_public_key = encode(&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());
|
|
||||||
|
|
||||||
// The timestamp and announced ip:port are kept as raw protocol bytes
|
// The timestamp and announced ip:port are kept as raw protocol bytes
|
||||||
// until this point so the offsets remain explicit.
|
// until this point so the offsets remain explicit.
|
||||||
|
|
@ -70,7 +63,7 @@ pub async fn parse_received_data(
|
||||||
Ok((
|
Ok((
|
||||||
received_message,
|
received_message,
|
||||||
received_signed_message,
|
received_signed_message,
|
||||||
received_address,
|
received_public_key,
|
||||||
hash,
|
hash,
|
||||||
received_ip,
|
received_ip,
|
||||||
peer_time,
|
peer_time,
|
||||||
|
|
@ -81,8 +74,7 @@ pub async fn generate_and_sign_message(
|
||||||
connection_type: &str,
|
connection_type: &str,
|
||||||
wallet: &Wallet,
|
wallet: &Wallet,
|
||||||
) -> Result<(String, String, String), String> {
|
) -> Result<(String, String, String), String> {
|
||||||
// get the wallet info so we can sign our return message
|
let public_key = wallet.saved.public_key.clone();
|
||||||
let address = wallet.saved.long_address.clone();
|
|
||||||
|
|
||||||
// if miner face is the return message, used because its hex so compressed better
|
// if miner face is the return message, used because its hex so compressed better
|
||||||
// otherwise face spelledbackwards is used
|
// otherwise face spelledbackwards is used
|
||||||
|
|
@ -93,7 +85,7 @@ pub async fn generate_and_sign_message(
|
||||||
let hashed = skein_256_hash_data(message);
|
let hashed = skein_256_hash_data(message);
|
||||||
let signed_message = Wallet::sign_transaction(&hashed, &wallet.saved.private_key).await;
|
let signed_message = Wallet::sign_transaction(&hashed, &wallet.saved.private_key).await;
|
||||||
Ok((
|
Ok((
|
||||||
address.to_string(),
|
public_key.to_string(),
|
||||||
message.to_string(),
|
message.to_string(),
|
||||||
signed_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 hashed = skein_256_hash_data(message);
|
||||||
let signed_message = Wallet::sign_transaction(&hashed, &wallet.saved.private_key).await;
|
let signed_message = Wallet::sign_transaction(&hashed, &wallet.saved.private_key).await;
|
||||||
Ok((
|
Ok((
|
||||||
address.to_string(),
|
public_key.to_string(),
|
||||||
message.to_string(),
|
message.to_string(),
|
||||||
signed_message.to_string(),
|
signed_message.to_string(),
|
||||||
))
|
))
|
||||||
|
|
@ -113,22 +105,23 @@ pub async fn generate_and_sign_message(
|
||||||
|
|
||||||
pub async fn return_handshake(
|
pub async fn return_handshake(
|
||||||
stream: Arc<Mutex<TcpStream>>,
|
stream: Arc<Mutex<TcpStream>>,
|
||||||
address: &str,
|
public_key: &str,
|
||||||
message: &str,
|
message: &str,
|
||||||
signed_message: &str,
|
signed_message: &str,
|
||||||
connections_key: &str,
|
connections_key: &str,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
// convert to binary/bytes
|
// 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 message_bin = decode(message).expect("Failed to decode message");
|
||||||
let signed_bin = decode(signed_message).expect("Failed to decode signed message");
|
let signed_bin = decode(signed_message).expect("Failed to decode signed message");
|
||||||
let mut data = Vec::with_capacity(HANDSHAKE_RESPONSE_BYTES);
|
let mut data = Vec::with_capacity(HANDSHAKE_RESPONSE_BYTES);
|
||||||
|
|
||||||
// Response frames mirror the request layout: challenge, signature,
|
// 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(&message_bin);
|
||||||
data.extend_from_slice(&signed_bin);
|
data.extend_from_slice(&signed_bin);
|
||||||
data.extend(&address_bin);
|
data.extend(&public_key_bin);
|
||||||
|
|
||||||
// get stream lock
|
// get stream lock
|
||||||
let mut stream_guard = stream.lock().await;
|
let mut stream_guard = stream.lock().await;
|
||||||
|
|
@ -164,13 +157,13 @@ pub async fn combine_and_send_data(params: CombineAndSendDataParams) -> Result<(
|
||||||
error!("Failed: {err}");
|
error!("Failed: {err}");
|
||||||
return Err(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
|
// The handshake response must complete before the command loop is
|
||||||
// spawned, otherwise the peer may start reading command data early.
|
// spawned, otherwise the peer may start reading command data early.
|
||||||
if let Err(err) = return_handshake(
|
if let Err(err) = return_handshake(
|
||||||
stream.clone(),
|
stream.clone(),
|
||||||
&address,
|
&public_key,
|
||||||
&message,
|
&message,
|
||||||
&signed_message,
|
&signed_message,
|
||||||
&connections_key,
|
&connections_key,
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ pub async fn verify_handshake(
|
||||||
_map: Arc<Mutex<Command>>,
|
_map: Arc<Mutex<Command>>,
|
||||||
hash: &str,
|
hash: &str,
|
||||||
received_signed_message: &str,
|
received_signed_message: &str,
|
||||||
received_address: &str,
|
received_public_key: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Signature verification proves the peer controls the wallet address
|
// Signature verification proves the peer controls the wallet address
|
||||||
// embedded in the handshake request.
|
// 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 padded_bytes = [hashmap_key[0], hashmap_key[1], hashmap_key[2], 0];
|
||||||
let uid = u32::from_le_bytes(padded_bytes);
|
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({
|
let response_bytes = RpcResponse::Binary({
|
||||||
"error: Handshake failed: Invalid handshake"
|
"error: Handshake failed: Invalid handshake"
|
||||||
.to_string()
|
.to_string()
|
||||||
|
|
|
||||||
|
|
@ -483,7 +483,7 @@ pub async fn start_loop(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let message = binary_to_string(message_bytes);
|
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,
|
&connections_key,
|
||||||
stream_locked.clone(),
|
stream_locked.clone(),
|
||||||
)
|
)
|
||||||
|
|
@ -495,7 +495,7 @@ pub async fn start_loop(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let result =
|
let result =
|
||||||
commands::validate_message::validate(message, address, signature).await;
|
commands::validate_message::validate(message, signature, address, &db).await;
|
||||||
result
|
result
|
||||||
.send(&stream_locked, Some(&connections_key), uid)
|
.send(&stream_locked, Some(&connections_key), uid)
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -724,7 +724,7 @@ pub async fn start_loop(
|
||||||
stream_locked.clone(),
|
stream_locked.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.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,
|
&connections_key,
|
||||||
stream_locked.clone(),
|
stream_locked.clone(),
|
||||||
)
|
)
|
||||||
|
|
@ -737,7 +737,7 @@ pub async fn start_loop(
|
||||||
|
|
||||||
let result = commands::wallet_register::register(
|
let result = commands::wallet_register::register(
|
||||||
short_address,
|
short_address,
|
||||||
long_address,
|
public_key,
|
||||||
signature,
|
signature,
|
||||||
&db,
|
&db,
|
||||||
map.clone(),
|
map.clone(),
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ pub enum HandshakeWallet {
|
||||||
},
|
},
|
||||||
// Wallet recovery tools already have the address and private key before the wallet is saved.
|
// Wallet recovery tools already have the address and private key before the wallet is saved.
|
||||||
WalletParts {
|
WalletParts {
|
||||||
long_address: String,
|
public_key: String,
|
||||||
private_key: String,
|
private_key: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ pub async fn connect_and_handshake(
|
||||||
hashmap_key: Byte3,
|
hashmap_key: Byte3,
|
||||||
) -> Result<Vec<u8>, io::Error> {
|
) -> Result<Vec<u8>, io::Error> {
|
||||||
// Resolve the wallet material before opening the stream so the handshake can sign its challenge.
|
// 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 {
|
HandshakeWallet::WalletKey {
|
||||||
encryption_key,
|
encryption_key,
|
||||||
wallet_path,
|
wallet_path,
|
||||||
|
|
@ -46,12 +46,12 @@ pub async fn connect_and_handshake(
|
||||||
let wallet = Wallet::try_obtain_wallet(encryption_key, Some(&wallet_path))
|
let wallet = Wallet::try_obtain_wallet(encryption_key, Some(&wallet_path))
|
||||||
.await
|
.await
|
||||||
.map_err(io::Error::other)?;
|
.map_err(io::Error::other)?;
|
||||||
(wallet.saved.long_address, wallet.saved.private_key)
|
(wallet.saved.public_key, wallet.saved.private_key)
|
||||||
}
|
}
|
||||||
HandshakeWallet::WalletParts {
|
HandshakeWallet::WalletParts {
|
||||||
long_address,
|
public_key,
|
||||||
private_key,
|
private_key,
|
||||||
} => (long_address, private_key),
|
} => (public_key, private_key),
|
||||||
};
|
};
|
||||||
|
|
||||||
let stream = TcpStream::connect(addr).await?;
|
let stream = TcpStream::connect(addr).await?;
|
||||||
|
|
@ -61,7 +61,7 @@ pub async fn connect_and_handshake(
|
||||||
stream,
|
stream,
|
||||||
json,
|
json,
|
||||||
rpc_command,
|
rpc_command,
|
||||||
long_address,
|
public_key,
|
||||||
private_key,
|
private_key,
|
||||||
hashmap_key,
|
hashmap_key,
|
||||||
)
|
)
|
||||||
|
|
@ -81,7 +81,7 @@ async fn perform_handshake(
|
||||||
mut stream: TcpStream,
|
mut stream: TcpStream,
|
||||||
json: String,
|
json: String,
|
||||||
rpc_command: usize,
|
rpc_command: usize,
|
||||||
address: String,
|
public_key: String,
|
||||||
private_key: String,
|
private_key: String,
|
||||||
hashmap_key: Byte3,
|
hashmap_key: Byte3,
|
||||||
) -> Result<Vec<u8>, io::Error> {
|
) -> Result<Vec<u8>, io::Error> {
|
||||||
|
|
@ -93,7 +93,8 @@ async fn perform_handshake(
|
||||||
let signed_message = Wallet::sign_transaction(&hash, &private_key).await;
|
let signed_message = Wallet::sign_transaction(&hash, &private_key).await;
|
||||||
|
|
||||||
// The peer expects the handshake as fixed-width binary fields in this order.
|
// 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 =
|
let message_bin =
|
||||||
decode(message).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
|
decode(message).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
|
||||||
let signed_bin =
|
let signed_bin =
|
||||||
|
|
@ -105,7 +106,7 @@ async fn perform_handshake(
|
||||||
// Build the complete client handshake packet.
|
// Build the complete client handshake packet.
|
||||||
data.extend_from_slice(&message_bin);
|
data.extend_from_slice(&message_bin);
|
||||||
data.extend_from_slice(&signed_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(&time_bin);
|
||||||
data.extend_from_slice(&ip);
|
data.extend_from_slice(&ip);
|
||||||
|
|
||||||
|
|
@ -172,24 +173,27 @@ async fn perform_handshake(
|
||||||
let response = &received;
|
let response = &received;
|
||||||
let returned_message_bin = &response[..HANDSHAKE_MESSAGE_BYTES];
|
let returned_message_bin = &response[..HANDSHAKE_MESSAGE_BYTES];
|
||||||
let returned_signed_bin = &response[HANDSHAKE_SIGNATURE_OFFSET..HANDSHAKE_ADDRESS_OFFSET];
|
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.
|
// Convert the returned binary fields into the formats used by wallet signature verification.
|
||||||
let returned_message = encode(returned_message_bin);
|
let returned_message = encode(returned_message_bin);
|
||||||
let returned_signed_message = encode(returned_signed_bin);
|
let returned_signed_message = encode(returned_signed_bin);
|
||||||
let complete_returned_address = Wallet::bytes_to_long_address(returned_address_bin.to_vec());
|
if returned_public_key_bin.len() != Wallet::PUBLIC_KEY_LENGTH {
|
||||||
if complete_returned_address.is_empty() {
|
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::InvalidData,
|
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.
|
// The server challenge must be the expected marker and its signature must match its address.
|
||||||
let hash = skein_256_hash_data(&returned_message);
|
let hash = skein_256_hash_data(&returned_message);
|
||||||
if returned_message == "ecaf" {
|
if returned_message == "ecaf" {
|
||||||
if Wallet::verify_transaction(&hash, &returned_signed_message, &complete_returned_address)
|
if Wallet::verify_transaction_with_public_key_bytes(
|
||||||
.await
|
&hash,
|
||||||
|
&returned_signed_message,
|
||||||
|
returned_public_key_bin,
|
||||||
|
)
|
||||||
|
.await
|
||||||
{
|
{
|
||||||
// At this point the handshake is complete, so the stream can carry the RPC request.
|
// At this point the handshake is complete, so the stream can carry the RPC request.
|
||||||
request(&mut stream, json, rpc_command, hashmap_key).await
|
request(&mut stream, json, rpc_command, hashmap_key).await
|
||||||
|
|
|
||||||
|
|
@ -404,30 +404,35 @@ async fn build_request_bytes(
|
||||||
bin_msg.extend_from_slice(&hashmap_key);
|
bin_msg.extend_from_slice(&hashmap_key);
|
||||||
bin_msg.extend_from_slice(&address_bytes);
|
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 => {
|
38 => {
|
||||||
let command_number: u8 = RPC_REGISTER_WALLET;
|
let command_number: u8 = RPC_REGISTER_WALLET;
|
||||||
let (short_address, rest) = command_input.split_once('|').ok_or_else(|| {
|
let (short_address, rest) = command_input.split_once('|').ok_or_else(|| {
|
||||||
io::Error::new(
|
io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
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::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
"Wallet registration input must be short|long|signature",
|
"Wallet registration input must be short|public_key|signature",
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let short_address_bytes =
|
let short_address_bytes =
|
||||||
Wallet::short_address_to_bytes(short_address).ok_or_else(|| {
|
Wallet::short_address_to_bytes(short_address).ok_or_else(|| {
|
||||||
io::Error::new(io::ErrorKind::InvalidInput, "Invalid short wallet address")
|
io::Error::new(io::ErrorKind::InvalidInput, "Invalid short wallet address")
|
||||||
})?;
|
})?;
|
||||||
let long_address_bytes = Wallet::long_address_to_bytes(long_address.to_string());
|
let public_key_bytes = decode(public_key).map_err(|err| {
|
||||||
if long_address_bytes.len() != Wallet::ADDRESS_BYTES_LENGTH {
|
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(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
"Invalid long wallet address",
|
"Invalid wallet registration public key",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let signature_bytes = decode(signature).map_err(|err| {
|
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(&command_number.to_le_bytes());
|
||||||
bin_msg.extend_from_slice(&hashmap_key);
|
bin_msg.extend_from_slice(&hashmap_key);
|
||||||
bin_msg.extend_from_slice(&short_address_bytes);
|
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);
|
bin_msg.extend_from_slice(&signature_bytes);
|
||||||
}
|
}
|
||||||
// Lookup remote balance by long, short, or vanity address.
|
// 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(),
|
vanity_address.to_string(),
|
||||||
RPC_VANITY_OWNER_LOOKUP as usize,
|
RPC_VANITY_OWNER_LOOKUP as usize,
|
||||||
HandshakeWallet::WalletParts {
|
HandshakeWallet::WalletParts {
|
||||||
long_address: wallet.saved.long_address.clone(),
|
public_key: wallet.saved.public_key.clone(),
|
||||||
private_key: wallet.saved.private_key.clone(),
|
private_key: wallet.saved.private_key.clone(),
|
||||||
},
|
},
|
||||||
generate_uid(),
|
generate_uid(),
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ async fn record_local_monitor_for_peer(
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let peer_long = Wallet::bytes_to_long_address(peer_wallet_bytes);
|
let Some(monitored_address) = Wallet::public_key_bytes_to_short_address(&peer_wallet_bytes)
|
||||||
let Some(monitored_address) = Wallet::normalize_to_short_address(&peer_long) else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let monitoring_address = wallet.saved.short_address.clone();
|
let monitoring_address = wallet.saved.short_address.clone();
|
||||||
|
|
@ -90,7 +90,7 @@ pub async fn announce_self_to_network(
|
||||||
Some(bytes) => bytes,
|
Some(bytes) => bytes,
|
||||||
None => return,
|
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 time = Utc::now().timestamp_millis() as u64;
|
||||||
let modified_timestamp_bytes = time.to_le_bytes();
|
let modified_timestamp_bytes = time.to_le_bytes();
|
||||||
// Self-announcement is intentionally unsigned. The receiving node
|
// Self-announcement is intentionally unsigned. The receiving node
|
||||||
|
|
@ -101,7 +101,7 @@ pub async fn announce_self_to_network(
|
||||||
1 + 3
|
1 + 3
|
||||||
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
|
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
|
||||||
+ 16
|
+ 16
|
||||||
+ Wallet::ADDRESS_BYTES_LENGTH
|
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
|
||||||
+ 8
|
+ 8
|
||||||
+ Wallet::SIGNATURE_LENGTH,
|
+ 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) {
|
let added_by = if added_by_bytes.iter().all(|&byte| byte == 0) {
|
||||||
String::new()
|
String::new()
|
||||||
} else {
|
} 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(
|
let added_timestamp = u64::from_le_bytes(
|
||||||
chunk[NODE_ADDED_TIMESTAMP_OFFSET..NODE_ADDED_SIGNATURE_OFFSET]
|
chunk[NODE_ADDED_TIMESTAMP_OFFSET..NODE_ADDED_SIGNATURE_OFFSET]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::FnDsaKeyPair;
|
||||||
use crate::{decode, encode};
|
use crate::{decode, encode};
|
||||||
|
|
||||||
impl Wallet {
|
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.
|
// Generate a new FN-DSA key pair using the wallet security parameter.
|
||||||
let keypair =
|
let keypair =
|
||||||
FnDsaKeyPair::generate(Self::FN_DSA_LOGN).expect("Failed to generate FN-DSA key pair");
|
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.
|
// Keep the private key as raw bytes until it is hex-encoded for storage.
|
||||||
let secret_key_bytes = keypair.private_key().to_vec();
|
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();
|
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.
|
// Private keys are persisted as hex before wallet-image encryption.
|
||||||
let private_key_hex = encode(secret_key_bytes);
|
let private_key_hex = encode(secret_key_bytes);
|
||||||
|
|
||||||
// Return the network-tagged public key bytes and hex private key.
|
(public_key_bytes, private_key_hex)
|
||||||
(encoded_public_key, private_key_hex)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn regenerate_public_key(private_key_hex: &str) -> Result<Vec<u8>, String> {
|
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)
|
let keypair = FnDsaKeyPair::from_private_key(&private_key_bytes)
|
||||||
.map_err(|e| format!("Failed to decode the provided FN-DSA private key: {e}"))?;
|
.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 _ = network_byte;
|
||||||
let mut encoded_public_key = Vec::with_capacity(Self::ADDRESS_BYTES_LENGTH);
|
Ok(keypair.public_key().to_vec())
|
||||||
encoded_public_key.push(network_byte);
|
|
||||||
encoded_public_key.extend_from_slice(keypair.public_key());
|
|
||||||
Ok(encoded_public_key)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,17 @@
|
||||||
|
use crate::encode;
|
||||||
use crate::wallets::structures::SavedWallet;
|
use crate::wallets::structures::SavedWallet;
|
||||||
use crate::wallets::structures::Wallet;
|
use crate::wallets::structures::Wallet;
|
||||||
use crate::Path;
|
use crate::Path;
|
||||||
use crate::{create_img, encrypts};
|
use crate::{create_img, encrypts};
|
||||||
use crate::{decode, encode};
|
|
||||||
|
|
||||||
impl Wallet {
|
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 {
|
pub fn create_wallet(&self, network_byte: u8) -> Wallet {
|
||||||
// Generate a fresh FN-DSA key pair for the selected network.
|
// Generate a fresh FN-DSA key pair for the selected network.
|
||||||
let (public_key, private_key) = Self::generate_keypair(network_byte);
|
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.
|
// Hash the public key body into the fixed 22-byte short-address format.
|
||||||
let short_address_bytes =
|
let short_address_bytes = Self::public_key_bytes_to_short_address_bytes(&public_key)
|
||||||
Self::long_address_bytes_to_short_address_bytes(&long_address_bytes)
|
.expect("Failed to derive short address from generated wallet");
|
||||||
.expect("Failed to derive short address from generated wallet");
|
|
||||||
|
|
||||||
// Convert the short-address bytes into the saved text form.
|
// Convert the short-address bytes into the saved text form.
|
||||||
let short_address = Self::bytes_to_short_address(&short_address_bytes)
|
let short_address = Self::bytes_to_short_address(&short_address_bytes)
|
||||||
|
|
@ -82,7 +45,6 @@ impl Wallet {
|
||||||
|
|
||||||
// Build the wallet data saved to disk.
|
// Build the wallet data saved to disk.
|
||||||
let saved = SavedWallet {
|
let saved = SavedWallet {
|
||||||
long_address,
|
|
||||||
short_address,
|
short_address,
|
||||||
vanity_address: None,
|
vanity_address: None,
|
||||||
public_key: encode(public_key),
|
public_key: encode(public_key),
|
||||||
|
|
@ -102,7 +64,6 @@ impl Wallet {
|
||||||
) -> Result<Wallet, String> {
|
) -> Result<Wallet, String> {
|
||||||
// Start with an empty saved wallet so create_wallet has an encryption key holder.
|
// Start with an empty saved wallet so create_wallet has an encryption key holder.
|
||||||
let new_wallet = SavedWallet {
|
let new_wallet = SavedWallet {
|
||||||
long_address: "".to_string(),
|
|
||||||
short_address: "".to_string(),
|
short_address: "".to_string(),
|
||||||
vanity_address: None,
|
vanity_address: None,
|
||||||
public_key: "".to_string(),
|
public_key: "".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,6 @@ impl Wallet {
|
||||||
return Self::bytes_to_vanity_address(&vanity_address_bytes);
|
return Self::bytes_to_vanity_address(&vanity_address_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat anything else as a possible long wallet address.
|
None
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ use crate::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct SavedWallet {
|
pub struct SavedWallet {
|
||||||
pub long_address: String,
|
|
||||||
pub short_address: String,
|
pub short_address: String,
|
||||||
pub vanity_address: Option<String>,
|
pub vanity_address: Option<String>,
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
|
|
@ -26,10 +25,7 @@ impl Wallet {
|
||||||
pub const SHORT_ADDRESS_SEPARATOR: u8 = b'.';
|
pub const SHORT_ADDRESS_SEPARATOR: u8 = b'.';
|
||||||
|
|
||||||
pub fn display_wallet(&self) -> String {
|
pub fn display_wallet(&self) -> String {
|
||||||
let mut output = format!(
|
let mut output = format!("Short Address: {}", self.saved.short_address);
|
||||||
"Long Address: {}\nShort Address: {}",
|
|
||||||
self.saved.long_address, self.saved.short_address
|
|
||||||
);
|
|
||||||
if let Some(vanity_address) = &self.saved.vanity_address {
|
if let Some(vanity_address) = &self.saved.vanity_address {
|
||||||
output.push_str(&format!("\nVanity Address: {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};
|
use fn_dsa::{VerifyingKey, VerifyingKeyStandard, DOMAIN_NONE, HASH_ID_RAW};
|
||||||
|
|
||||||
impl Wallet {
|
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(
|
pub async fn vrf_verify_with_public_key(
|
||||||
number: u128,
|
number: u128,
|
||||||
hash: &str,
|
hash: &str,
|
||||||
|
|
@ -33,30 +22,6 @@ impl Wallet {
|
||||||
false
|
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(
|
pub async fn verify_transaction_with_public_key(
|
||||||
message: &str,
|
message: &str,
|
||||||
signature_hex: &str,
|
signature_hex: &str,
|
||||||
|
|
@ -84,4 +49,27 @@ impl Wallet {
|
||||||
let message_bytes = decode(message).unwrap_or_else(|_| message.as_bytes().to_vec());
|
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)
|
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;
|
use crate::wallets::structures::Wallet;
|
||||||
|
|
||||||
impl 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.
|
// Public keys must match the FN-DSA public key byte length exactly.
|
||||||
if public_key_bytes.len() != Self::PUBLIC_KEY_LENGTH {
|
if public_key_bytes.len() != Self::PUBLIC_KEY_LENGTH {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -11,41 +11,6 @@ impl Wallet {
|
||||||
public_key_bytes[0] == Self::FN_DSA_LOGN as u8
|
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 {
|
pub fn short_address_validation(short_address: &str) -> bool {
|
||||||
// Accept either canonical short-address bytes or vanity-address bytes.
|
// Accept either canonical short-address bytes or vanity-address bytes.
|
||||||
let short_address_bytes = match Self::short_address_to_bytes(short_address)
|
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::common::skein::{ripemd160_hash_bytes, skein_256_hash_bytes};
|
||||||
use crate::log::error;
|
|
||||||
use crate::wallets::structures::Wallet;
|
use crate::wallets::structures::Wallet;
|
||||||
use crate::{decode, encode};
|
use crate::{decode, encode};
|
||||||
|
|
||||||
impl Wallet {
|
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> {
|
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.
|
// Short-address bytes must contain a 20-byte payload, separator, and network byte.
|
||||||
if short_address_bytes.len() != Self::SHORT_ADDRESS_BYTES_LENGTH {
|
if short_address_bytes.len() != Self::SHORT_ADDRESS_BYTES_LENGTH {
|
||||||
|
|
@ -185,20 +114,13 @@ impl Wallet {
|
||||||
Some(format!("{payload}.{network_suffix}"))
|
Some(format!("{payload}.{network_suffix}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn long_address_bytes_to_short_address_bytes(long_address_bytes: &[u8]) -> Option<Vec<u8>> {
|
pub fn public_key_bytes_to_short_address_bytes(public_key_bytes: &[u8]) -> Option<Vec<u8>> {
|
||||||
// Long-address bytes must contain the network byte and public key body.
|
if public_key_bytes.len() != Self::PUBLIC_KEY_LENGTH {
|
||||||
if long_address_bytes.len() != Self::ADDRESS_BYTES_LENGTH {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep the source network byte as the short-address network byte.
|
let network_byte = Self::current_network_byte();
|
||||||
let network_byte = long_address_bytes[0];
|
let skein_hash_hex = skein_256_hash_bytes(public_key_bytes);
|
||||||
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 skein_hash_bytes = decode(&skein_hash_hex).ok()?;
|
let skein_hash_bytes = decode(&skein_hash_hex).ok()?;
|
||||||
|
|
||||||
// RIPEMD-160 produces the 20-byte short-address payload.
|
// RIPEMD-160 produces the 20-byte short-address payload.
|
||||||
|
|
@ -217,6 +139,22 @@ impl Wallet {
|
||||||
Some(short_address)
|
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>> {
|
pub fn sanitize_vanity_address_bytes(payload: &str) -> Option<Vec<u8>> {
|
||||||
// Vanity payloads are stored as exactly 20 ASCII bytes.
|
// Vanity payloads are stored as exactly 20 ASCII bytes.
|
||||||
if payload.len() != Self::SHORT_ADDRESS_HASH_BYTES_LENGTH {
|
if payload.len() != Self::SHORT_ADDRESS_HASH_BYTES_LENGTH {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue