218 lines
7.8 KiB
Rust
218 lines
7.8 KiB
Rust
use crate::common::skein::skein_256_hash_data;
|
|
use crate::records::memory::mempool::DB;
|
|
use crate::to_string;
|
|
use crate::wallets::structures::Wallet;
|
|
use crate::Cursor;
|
|
use crate::Serialize;
|
|
use crate::{decode, encode};
|
|
use crate::{AsyncReadExt, AsyncWriteExt};
|
|
|
|
#[derive(Debug, Serialize, Clone)] // 57 bytes
|
|
pub struct UnsignedVanityAddressTransaction {
|
|
pub txtype: u8, // 1 byte transaction type, should be 12
|
|
pub timestamp: u32, // 4 bytes transaction timestamp
|
|
pub address: String, // 22 bytes real short address receiving the vanity mapping
|
|
pub vanity_address: String, // 22 bytes vanity short address being registered
|
|
pub txfee: u64, // 8 bytes fee paid for vanity registration
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Clone)] // 723 bytes
|
|
pub struct VanityAddressTransaction {
|
|
pub unsigned_vanity_address: UnsignedVanityAddressTransaction, // 57 bytes
|
|
pub signature: String, // 666 bytes
|
|
}
|
|
|
|
impl VanityAddressTransaction {
|
|
pub const BYTE_LENGTH: usize = 1
|
|
+ 4
|
|
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
|
|
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
|
|
+ 8
|
|
+ Wallet::SIGNATURE_LENGTH;
|
|
}
|
|
|
|
impl UnsignedVanityAddressTransaction {
|
|
// Create the unsigned vanity-address registration payload.
|
|
pub async fn new(
|
|
txtype: u8,
|
|
timestamp: u32,
|
|
address: &str,
|
|
vanity_address: &str,
|
|
txfee: u64,
|
|
) -> Self {
|
|
Self {
|
|
txtype,
|
|
timestamp,
|
|
address: address.to_string(),
|
|
vanity_address: vanity_address.to_string(),
|
|
txfee,
|
|
}
|
|
}
|
|
|
|
pub async fn hash(&self) -> String {
|
|
// Hash the unsigned payload for verification and mempool indexing.
|
|
let serialized = to_string(self).unwrap();
|
|
skein_256_hash_data(&serialized)
|
|
}
|
|
|
|
pub async fn hash_and_sign(
|
|
&self,
|
|
private_key: &str,
|
|
) -> Result<String, Box<dyn std::error::Error>> {
|
|
// Sign the unsigned vanity transaction with the registering wallet.
|
|
let serialized = to_string(self)?;
|
|
let hash = skein_256_hash_data(&serialized);
|
|
let signature = Wallet::sign_transaction(&hash, private_key).await;
|
|
Ok(signature)
|
|
}
|
|
}
|
|
|
|
impl VanityAddressTransaction {
|
|
pub async fn new(
|
|
unsigned_vanity_address: UnsignedVanityAddressTransaction,
|
|
private_key: &str,
|
|
) -> Result<Self, Box<dyn std::error::Error>> {
|
|
// Create a signed vanity-address transaction.
|
|
let signature = unsigned_vanity_address.hash_and_sign(private_key).await?;
|
|
Ok(Self {
|
|
unsigned_vanity_address,
|
|
signature,
|
|
})
|
|
}
|
|
|
|
pub async fn load(
|
|
unsigned_vanity_address: UnsignedVanityAddressTransaction,
|
|
signature: &str,
|
|
) -> Self {
|
|
// Rebuild a vanity transaction already read from storage.
|
|
Self {
|
|
unsigned_vanity_address,
|
|
signature: signature.to_string(),
|
|
}
|
|
}
|
|
|
|
pub async fn to_bytes(&self) -> tokio::io::Result<Vec<u8>> {
|
|
// Serialize into the fixed vanity transaction byte layout.
|
|
let mut buffer = Vec::with_capacity(Self::BYTE_LENGTH);
|
|
let mut cursor = Cursor::new(&mut buffer);
|
|
|
|
cursor
|
|
.write_all(&self.unsigned_vanity_address.txtype.to_le_bytes())
|
|
.await?;
|
|
cursor
|
|
.write_all(&self.unsigned_vanity_address.timestamp.to_le_bytes())
|
|
.await?;
|
|
|
|
let address_bytes =
|
|
Wallet::short_address_to_bytes(&self.unsigned_vanity_address.address)
|
|
.ok_or_else(|| tokio::io::Error::other("Invalid sender short address"))?;
|
|
// Vanity addresses use the same 22-byte width as short addresses
|
|
// but are encoded through the vanity-specific byte conversion.
|
|
let vanity_bytes =
|
|
Wallet::vanity_address_to_bytes(&self.unsigned_vanity_address.vanity_address)
|
|
.ok_or_else(|| tokio::io::Error::other("Invalid vanity short address"))?;
|
|
|
|
cursor.write_all(&address_bytes).await?;
|
|
cursor.write_all(&vanity_bytes).await?;
|
|
cursor
|
|
.write_all(&self.unsigned_vanity_address.txfee.to_le_bytes())
|
|
.await?;
|
|
let signature_bytes = decode(&self.signature).map_err(|err| {
|
|
tokio::io::Error::other(format!("Invalid vanity signature hex: {err}"))
|
|
})?;
|
|
cursor.write_all(&signature_bytes).await?;
|
|
|
|
Ok(buffer)
|
|
}
|
|
|
|
pub async fn from_bytes(txtype: u8, bytes: &[u8]) -> tokio::io::Result<Self> {
|
|
// The block parser already consumed the transaction type byte.
|
|
if bytes.len() != Self::BYTE_LENGTH - 1 {
|
|
return Err(tokio::io::Error::other("Invalid Byte Count"));
|
|
}
|
|
|
|
let mut cursor = Cursor::new(bytes);
|
|
|
|
// Decode timestamp, real short address, vanity address, fee, and signature.
|
|
let timestamp = cursor.read_u32_le().await?;
|
|
|
|
let mut address_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
|
|
cursor.read_exact(&mut address_bytes).await?;
|
|
let address = Wallet::bytes_to_short_address(&address_bytes)
|
|
.ok_or_else(|| tokio::io::Error::other("Invalid sender short address bytes"))?;
|
|
|
|
let mut vanity_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
|
|
cursor.read_exact(&mut vanity_bytes).await?;
|
|
let vanity_address = Wallet::bytes_to_vanity_address(&vanity_bytes)
|
|
.ok_or_else(|| tokio::io::Error::other("Invalid vanity short address bytes"))?;
|
|
|
|
let txfee = cursor.read_u64_le().await?;
|
|
|
|
let mut signature_bytes = vec![0; Wallet::SIGNATURE_LENGTH];
|
|
cursor.read_exact(&mut signature_bytes).await?;
|
|
let signature = encode(&signature_bytes);
|
|
|
|
let unsigned_vanity_address = UnsignedVanityAddressTransaction {
|
|
txtype,
|
|
timestamp,
|
|
address,
|
|
vanity_address,
|
|
txfee,
|
|
};
|
|
|
|
Ok(Self {
|
|
unsigned_vanity_address,
|
|
signature,
|
|
})
|
|
}
|
|
|
|
pub async fn add_to_memory(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
// Store the original bytes so the mempool can later rebuild the
|
|
// exact transaction submitted by the wallet.
|
|
let original_data = self.to_bytes().await?;
|
|
|
|
// PostgreSQL uses signed integer types for these persisted fields.
|
|
let time = self.unsigned_vanity_address.timestamp as i32;
|
|
let fee = i64::try_from(self.unsigned_vanity_address.txfee)
|
|
.map_err(|_| std::io::Error::other("Vanity fee exceeds i64 mempool limit"))?;
|
|
let address = &self.unsigned_vanity_address.address;
|
|
let vanity_address = &self.unsigned_vanity_address.vanity_address;
|
|
let hash = &self.unsigned_vanity_address.hash().await;
|
|
let signature = &self.signature;
|
|
|
|
// Vanity transactions are written to the vanity mempool table
|
|
// until the transaction is mined or removed.
|
|
let client = DB.get().ok_or_else(|| {
|
|
Box::new(std::io::Error::other("DB not initialized"))
|
|
as Box<dyn std::error::Error + Send + Sync>
|
|
})?;
|
|
|
|
client
|
|
.execute(
|
|
r#"
|
|
INSERT INTO vanity_address (
|
|
time,
|
|
fee,
|
|
address,
|
|
vanity_address,
|
|
hash,
|
|
signature,
|
|
original
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
"#,
|
|
&[
|
|
&time,
|
|
&fee,
|
|
address,
|
|
vanity_address,
|
|
hash,
|
|
signature,
|
|
&original_data,
|
|
],
|
|
)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|