Fix staged torrent candidate race

This commit is contained in:
viraladmin 2026-05-26 00:24:57 -06:00
parent f92823ac90
commit 61a64cf538
131 changed files with 5957 additions and 5644 deletions

View File

@ -11,10 +11,7 @@ use blockchain::{create_dir_all, AsyncWriteExt};
// Pad the asset name so it matches the 15-byte on-chain asset format.
fn pad_to_width(input: &str, width: usize) -> String {
let mut result = String::with_capacity(width);
let _ = std::fmt::write(
&mut result,
format_args!("{input:<width$}"),
);
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
result
}

View File

@ -10,10 +10,7 @@ use blockchain::{create_dir_all, AsyncWriteExt};
// Pad the ticker so it matches the fixed-width 15-byte on-chain format.
fn pad_to_width(input: &str, width: usize) -> String {
let mut result = String::with_capacity(width);
let _ = std::fmt::write(
&mut result,
format_args!("{input:<width$}"),
);
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
result
}

View File

@ -11,10 +11,7 @@ use blockchain::{Local, LocalResult, NaiveDate, NaiveTime, TimeZone};
fn pad_to_width(input: &str, width: usize) -> String {
// Asset names are fixed-width fields in the loan transaction bytes.
let mut result = String::with_capacity(width);
let _ = std::fmt::write(
&mut result,
format_args!("{input:<width$}"),
);
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
result
}

View File

@ -10,10 +10,7 @@ use blockchain::{create_dir_all, AsyncWriteExt};
// pad the coin to ensure 15 characters
fn pad_to_width(input: &str, width: usize) -> String {
let mut result = String::with_capacity(width); // Pre-allocate string with capacity
let _ = std::fmt::write(
&mut result,
format_args!("{input:<width$}"),
);
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
result
}

View File

@ -40,9 +40,7 @@ async fn main() {
// Refuse to overwrite an existing wallet file.
if let Ok(metadata) = metadata(&wallet_path).await {
if metadata.is_file() {
eprintln!(
"Error: Wallet already exists at the specified path: {wallet_path:?}"
);
eprintln!("Error: Wallet already exists at the specified path: {wallet_path:?}");
std::process::exit(1);
}
}

View File

@ -10,10 +10,7 @@ use blockchain::{create_dir_all, AsyncWriteExt};
// pad the coin to ensure 15 characters
fn pad_to_width(input: &str, width: usize) -> String {
let mut result = String::with_capacity(width); // Pre-allocate string with capacity
let _ = std::fmt::write(
&mut result,
format_args!("{input:<width$}"),
);
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
result
}

View File

@ -12,10 +12,7 @@ use blockchain::{create_dir_all, AsyncWriteExt};
// pad the coin to ensure 15 characters
fn pad_to_width(input: &str, width: usize) -> String {
let mut result = String::with_capacity(width); // Pre-allocate string with capacity
let _ = std::fmt::write(
&mut result,
format_args!("{input:<width$}"),
);
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
result
}

View File

@ -10,10 +10,7 @@ use blockchain::{create_dir_all, AsyncWriteExt};
// pad the coin to ensure 15 characters
fn pad_to_width(input: &str, width: usize) -> String {
let mut result = String::with_capacity(width); // Pre-allocate string with capacity
let _ = std::fmt::write(
&mut result,
format_args!("{input:<width$}"),
);
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
result
}

View File

@ -13,10 +13,7 @@ use blockchain::{create_dir_all, AsyncWriteExt};
// pad the coin to ensure 15 characters
fn pad_to_width(input: &str, width: usize) -> String {
let mut result = String::with_capacity(width); // Pre-allocate string with capacity
let _ = std::fmt::write(
&mut result,
format_args!("{input:<width$}"),
);
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
result
}

View File

@ -26,17 +26,50 @@ async fn decode_one_transaction(tx_bytes: &[u8]) -> Option<String> {
let body = &tx_bytes[1..];
match txtype {
TRANSFER_TYPE => to_string_pretty(&TransferTransaction::from_bytes(txtype, body).await.ok()?).ok(),
CREATE_TOKEN_TYPE => to_string_pretty(&CreateTokenTransaction::from_bytes(txtype, body).await.ok()?).ok(),
CREATE_NFT_TYPE => to_string_pretty(&CreateNftTransaction::from_bytes(txtype, body).await.ok()?).ok(),
MARKETING_TYPE => to_string_pretty(&MarketingTransaction::from_bytes(txtype, body).await.ok()?).ok(),
TRANSFER_TYPE => {
to_string_pretty(&TransferTransaction::from_bytes(txtype, body).await.ok()?).ok()
}
CREATE_TOKEN_TYPE => to_string_pretty(
&CreateTokenTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
CREATE_NFT_TYPE => {
to_string_pretty(&CreateNftTransaction::from_bytes(txtype, body).await.ok()?).ok()
}
MARKETING_TYPE => {
to_string_pretty(&MarketingTransaction::from_bytes(txtype, body).await.ok()?).ok()
}
SWAP_TYPE => to_string_pretty(&SwapTransaction::from_bytes(txtype, body).await.ok()?).ok(),
LENDER_TYPE => to_string_pretty(&LoanContractTransaction::from_bytes(txtype, body).await.ok()?).ok(),
BORROWER_TYPE => to_string_pretty(&ContractPaymentTransaction::from_bytes(txtype, body).await.ok()?).ok(),
COLLATERAL_TYPE => to_string_pretty(&CollateralClaimTransaction::from_bytes(txtype, body).await.ok()?).ok(),
LENDER_TYPE => to_string_pretty(
&LoanContractTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
BORROWER_TYPE => to_string_pretty(
&ContractPaymentTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
COLLATERAL_TYPE => to_string_pretty(
&CollateralClaimTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
BURN_TYPE => to_string_pretty(&BurnTransaction::from_bytes(txtype, body).await.ok()?).ok(),
ISSUE_TOKEN_TYPE => to_string_pretty(&IssueTokenTransaction::from_bytes(txtype, body).await.ok()?).ok(),
VANITY_ADDRESS_TYPE => to_string_pretty(&VanityAddressTransaction::from_bytes(txtype, body).await.ok()?).ok(),
ISSUE_TOKEN_TYPE => {
to_string_pretty(&IssueTokenTransaction::from_bytes(txtype, body).await.ok()?).ok()
}
VANITY_ADDRESS_TYPE => to_string_pretty(
&VanityAddressTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
_ => None,
}
}

View File

@ -27,17 +27,50 @@ async fn decode_mempool_transaction(response: &[u8]) -> Option<String> {
let body = &response[1..];
match txtype {
TRANSFER_TYPE => to_string_pretty(&TransferTransaction::from_bytes(txtype, body).await.ok()?).ok(),
CREATE_TOKEN_TYPE => to_string_pretty(&CreateTokenTransaction::from_bytes(txtype, body).await.ok()?).ok(),
CREATE_NFT_TYPE => to_string_pretty(&CreateNftTransaction::from_bytes(txtype, body).await.ok()?).ok(),
MARKETING_TYPE => to_string_pretty(&MarketingTransaction::from_bytes(txtype, body).await.ok()?).ok(),
TRANSFER_TYPE => {
to_string_pretty(&TransferTransaction::from_bytes(txtype, body).await.ok()?).ok()
}
CREATE_TOKEN_TYPE => to_string_pretty(
&CreateTokenTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
CREATE_NFT_TYPE => {
to_string_pretty(&CreateNftTransaction::from_bytes(txtype, body).await.ok()?).ok()
}
MARKETING_TYPE => {
to_string_pretty(&MarketingTransaction::from_bytes(txtype, body).await.ok()?).ok()
}
SWAP_TYPE => to_string_pretty(&SwapTransaction::from_bytes(txtype, body).await.ok()?).ok(),
LENDER_TYPE => to_string_pretty(&LoanContractTransaction::from_bytes(txtype, body).await.ok()?).ok(),
BORROWER_TYPE => to_string_pretty(&ContractPaymentTransaction::from_bytes(txtype, body).await.ok()?).ok(),
COLLATERAL_TYPE => to_string_pretty(&CollateralClaimTransaction::from_bytes(txtype, body).await.ok()?).ok(),
LENDER_TYPE => to_string_pretty(
&LoanContractTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
BORROWER_TYPE => to_string_pretty(
&ContractPaymentTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
COLLATERAL_TYPE => to_string_pretty(
&CollateralClaimTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
BURN_TYPE => to_string_pretty(&BurnTransaction::from_bytes(txtype, body).await.ok()?).ok(),
ISSUE_TOKEN_TYPE => to_string_pretty(&IssueTokenTransaction::from_bytes(txtype, body).await.ok()?).ok(),
VANITY_ADDRESS_TYPE => to_string_pretty(&VanityAddressTransaction::from_bytes(txtype, body).await.ok()?).ok(),
ISSUE_TOKEN_TYPE => {
to_string_pretty(&IssueTokenTransaction::from_bytes(txtype, body).await.ok()?).ok()
}
VANITY_ADDRESS_TYPE => to_string_pretty(
&VanityAddressTransaction::from_bytes(txtype, body)
.await
.ok()?,
)
.ok(),
_ => None,
}
}

View File

@ -45,10 +45,9 @@ fn decode_network_info(response: &[u8]) -> Option<String> {
// Mainnet uses CLC and testnet uses CLTC, so the prefix length is
// inferred from the total payload size instead of hard-coded.
let wallet_prefix =
String::from_utf8_lossy(response.get(offset..offset + wallet_prefix_len)?)
.trim()
.to_string();
let wallet_prefix = String::from_utf8_lossy(response.get(offset..offset + wallet_prefix_len)?)
.trim()
.to_string();
offset += wallet_prefix_len;
let height = read_u32(response, &mut offset)?;

View File

@ -89,9 +89,7 @@ async fn main() {
let address_file_path = if args.len() == 2 {
args[1].clone()
} else {
match prompt_for_path(
"Please enter the path to the file containing the wallet address: ",
) {
match prompt_for_path("Please enter the path to the file containing the wallet address: ") {
Ok(path) => path,
Err(err) => {
eprintln!("{err}");

View File

@ -1,5 +1,5 @@
use blockchain::common::skein::{
skein_256_hash_data, skein_256_hash_bytes, skein_128_hash_bytes, skein_128_hash_data,
skein_128_hash_bytes, skein_128_hash_data, skein_256_hash_bytes, skein_256_hash_data,
};
use blockchain::env;
use blockchain::File;

View File

@ -1,6 +1,6 @@
use blockchain::common::binary_conversions::hex_to_u64;
use blockchain::common::network_paths_and_settings::block_extension_and_paths;
use blockchain::common::skein::{skein_256_hash_data, skein_128_hash_bytes};
use blockchain::common::skein::{skein_128_hash_bytes, skein_256_hash_data};
use blockchain::encode;
use blockchain::env;
use blockchain::records::unpack_block::unpack_header::load_block_header;

View File

@ -83,9 +83,7 @@ async fn main() {
let address_path = if args.len() == 2 {
args[1].clone()
} else {
match prompt_for_path(
"Please enter the path to the file containing the wallet address: ",
) {
match prompt_for_path("Please enter the path to the file containing the wallet address: ") {
Ok(path) => path,
Err(err) => {
eprintln!("{err}");

View File

@ -118,9 +118,8 @@ async fn main() {
"Do you agree that payments should be made {}?",
display_payment_period(&payment_period)
);
let question4 = format!(
"Do you agree that this loan requires {payment_number} total payments?"
);
let question4 =
format!("Do you agree that this loan requires {payment_number} total payments?");
let question5 = format!(
"Do you agree that each payment should be {} {}?",
display_amount(payment_amount),

View File

@ -12,10 +12,7 @@ use blockchain::Value;
// padd the coin to ensure 15 characters
fn pad_to_width(input: &str, width: usize) -> String {
let mut result = String::with_capacity(width); // Pre-allocate string with capacity
let _ = std::fmt::write(
&mut result,
format_args!("{input:<width$}"),
);
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
result
}
@ -138,13 +135,9 @@ async fn main() {
_log_path,
) = block_extension_and_paths();
// setup validation questions
let question1 = format!(
"Are you expecting to receive {receive_amount} {token_name1}?"
);
let question1 = format!("Are you expecting to receive {receive_amount} {token_name1}?");
let question2 = format!("Are you expecting to send {send_amount} {token_name2}?");
let question3 = format!(
"Are you willing to spend {txfee2} {network_coin} in fees?"
);
let question3 = format!("Are you willing to spend {txfee2} {network_coin} in fees?");
let question4 = format!("Are you willing to tip {tip2} {token_name2}?");
// ask validation questions

View File

@ -185,10 +185,8 @@ impl VrfBlock {
cursor
.write_all(&self.unmined_block.timestamp.to_le_bytes())
.await?;
let miner_bytes =
Wallet::short_address_to_bytes(&self.unmined_block.miner).ok_or_else(|| {
tokio::io::Error::other("Invalid short miner address")
})?;
let miner_bytes = Wallet::short_address_to_bytes(&self.unmined_block.miner)
.ok_or_else(|| tokio::io::Error::other("Invalid short miner address"))?;
cursor.write_all(&miner_bytes).await?;
cursor
.write_all(&decode(&self.unmined_block.previous_hash).unwrap())
@ -207,8 +205,7 @@ impl VrfBlock {
pub async fn from_bytes(bytes: &[u8]) -> tokio::io::Result<Self> {
// A VRF header must be exactly the fixed header byte length.
if bytes.len() != VRF_BLOCK_BYTES {
return Err(tokio::io::Error::other("Invalid Byte Count for Block",
));
return Err(tokio::io::Error::other("Invalid Byte Count for Block"));
}
// Read from the fixed-width VRF header bytes.
@ -219,9 +216,8 @@ impl VrfBlock {
let mut miner_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut miner_bytes).await?;
let miner = Wallet::bytes_to_short_address(&miner_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid short miner address")
})?;
let miner = Wallet::bytes_to_short_address(&miner_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid short miner address"))?;
// Decode parent hash, difficulty, nonce, VRF number, and proof.
let mut prev_hash_bytes = vec![0; 32];

View File

@ -105,9 +105,7 @@ impl BurnTransaction {
.write_all(&self.unsigned_burn.time.to_le_bytes())
.await?;
let address_bytes = Wallet::short_address_to_bytes(&self.unsigned_burn.address)
.ok_or_else(|| {
tokio::io::Error::other("Invalid burn short address")
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid burn short address"))?;
cursor.write_all(&address_bytes).await?;
cursor.write_all(self.unsigned_burn.coin.as_bytes()).await?;
cursor
@ -126,8 +124,7 @@ impl BurnTransaction {
pub async fn from_bytes(txtype: u8, bytes: &[u8]) -> tokio::io::Result<Self> {
if bytes.len() != Self::BYTE_LENGTH - 1 {
return Err(tokio::io::Error::other("Invalid Byte Count",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
let mut cursor = Cursor::new(bytes);
@ -136,10 +133,8 @@ impl BurnTransaction {
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 burn short address bytes",
)
})?;
let address = Wallet::bytes_to_short_address(&address_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid burn short address bytes"))?;
let mut coin_bytes = vec![0; 15];
cursor.read_exact(&mut coin_bytes).await?;

View File

@ -110,10 +110,7 @@ impl CollateralClaimTransaction {
.write_all(&decode(&self.unsigned_collateral_claim.contract_hash).unwrap())
.await?;
let address_bytes = Wallet::short_address_to_bytes(&self.unsigned_collateral_claim.address)
.ok_or_else(|| {
tokio::io::Error::other("Invalid collateral claimant short address",
)
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid collateral claimant short address"))?;
cursor.write_all(&address_bytes).await?;
cursor
.write_all(&self.unsigned_collateral_claim.txfee.to_le_bytes())
@ -126,8 +123,7 @@ impl CollateralClaimTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read the remaining fixed-width collateral-claim bytes.
@ -144,8 +140,7 @@ impl CollateralClaimTransaction {
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 collateral claimant short address bytes",
)
tokio::io::Error::other("Invalid collateral claimant short address bytes")
})?;
let txfee = cursor.read_u64_le().await?;

View File

@ -65,8 +65,7 @@ impl GenesisTransaction {
// The transaction type is read by the block parser, so this
// function receives only the remaining genesis bytes.
if bytes.len() != Self::BYTE_LENGTH - 1 {
return Err(tokio::io::Error::other("Invalid Byte Count",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read from the remaining fixed-width transaction bytes.

View File

@ -104,10 +104,7 @@ impl IssueTokenTransaction {
.write_all(&self.unsigned_issue_token.time.to_le_bytes())
.await?;
let creator_bytes = Wallet::short_address_to_bytes(&self.unsigned_issue_token.creator)
.ok_or_else(|| {
tokio::io::Error::other("Invalid issue-token creator short address",
)
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid issue-token creator short address"))?;
cursor.write_all(&creator_bytes).await?;
cursor
.write_all(self.unsigned_issue_token.ticker.as_bytes())
@ -126,8 +123,7 @@ impl IssueTokenTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
let mut cursor = Cursor::new(bytes);
@ -138,8 +134,7 @@ impl IssueTokenTransaction {
let mut creator_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut creator_bytes).await?;
let creator = Wallet::bytes_to_short_address(&creator_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid issue-token creator short address bytes",
)
tokio::io::Error::other("Invalid issue-token creator short address bytes")
})?;
// Decode token ticker, issued amount, fee, and signature.

View File

@ -123,9 +123,7 @@ impl ContractPaymentTransaction {
.write_all(&decode(&self.unsigned_contract_payment.contract_hash).unwrap())
.await?;
let address_bytes = Wallet::short_address_to_bytes(&self.unsigned_contract_payment.address)
.ok_or_else(|| {
tokio::io::Error::other("Invalid payer short address")
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid payer short address"))?;
cursor.write_all(&address_bytes).await?;
cursor
.write_all(&self.unsigned_contract_payment.tip.to_le_bytes())
@ -142,8 +140,7 @@ impl ContractPaymentTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read the remaining fixed-width loan-payment bytes.
@ -160,10 +157,8 @@ impl ContractPaymentTransaction {
// Decode payer short address, miner tip, fee, txid hash, and signature.
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 payer short address bytes",
)
})?;
let address = Wallet::bytes_to_short_address(&address_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid payer short address bytes"))?;
let tip = cursor.read_u64_le().await?;
let txfee = cursor.read_u64_le().await?;

View File

@ -21,9 +21,9 @@ pub struct UnsignedLoanContractTransaction {
pub payment_period: String, // 1 byte d, w, or m for days, weeks, or months
pub payment_number: u8, // 1 byte total number of payments
pub payment_amount: u64, // 8 bytes amount due for each payment
pub grace_period: u8, // 1 byte missed payments before collateral claim is allowed
pub max_late_value: u64, // 8 bytes max overdue value before collateral claim is allowed
pub txfee: u64, // 8 bytes transaction fee paid by the lender
pub grace_period: u8, // 1 byte missed payments before collateral claim is allowed
pub max_late_value: u64, // 8 bytes max overdue value before collateral claim is allowed
pub txfee: u64, // 8 bytes transaction fee paid by the lender
}
#[derive(Debug, Serialize, Clone)] // 1486 bytes
@ -35,9 +35,23 @@ pub struct LoanContractTransaction {
}
impl LoanContractTransaction {
pub const BYTE_LENGTH: usize =
1 + 4 + 15 + 8 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + 15 + 8 + Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ 1 + 1 + 8 + 1 + 8 + 8 + 32 + Wallet::SIGNATURE_LENGTH + Wallet::SIGNATURE_LENGTH;
pub const BYTE_LENGTH: usize = 1
+ 4
+ 15
+ 8
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ 15
+ 8
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ 1
+ 1
+ 8
+ 1
+ 8
+ 8
+ 32
+ Wallet::SIGNATURE_LENGTH
+ Wallet::SIGNATURE_LENGTH;
}
impl UnsignedLoanContractTransaction {
@ -163,9 +177,7 @@ impl LoanContractTransaction {
.write_all(&self.unsigned_loan_contract.loan_amount.to_le_bytes())
.await?;
let lender_bytes = Wallet::short_address_to_bytes(&self.unsigned_loan_contract.lender)
.ok_or_else(|| {
tokio::io::Error::other("Invalid lender short address")
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid lender short address"))?;
cursor.write_all(&lender_bytes).await?;
cursor
.write_all(self.unsigned_loan_contract.collateral.as_bytes())
@ -174,10 +186,7 @@ impl LoanContractTransaction {
.write_all(&self.unsigned_loan_contract.collateral_amount.to_le_bytes())
.await?;
let borrower_bytes = Wallet::short_address_to_bytes(&self.unsigned_loan_contract.borrower)
.ok_or_else(|| {
tokio::io::Error::other("Invalid borrower short address",
)
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid borrower short address"))?;
cursor.write_all(&borrower_bytes).await?;
cursor
.write_all(self.unsigned_loan_contract.payment_period.as_bytes())
@ -207,8 +216,7 @@ impl LoanContractTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read the remaining fixed-width loan-contract bytes.
@ -226,10 +234,8 @@ impl LoanContractTransaction {
// Decode lender short address.
let mut lender_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut lender_bytes).await?;
let lender = Wallet::bytes_to_short_address(&lender_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid lender short address bytes",
)
})?;
let lender = Wallet::bytes_to_short_address(&lender_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid lender short address bytes"))?;
// Decode collateral asset and amount.
let mut collateral_bytes = vec![0; 15];
@ -241,10 +247,8 @@ impl LoanContractTransaction {
// Decode borrower short address.
let mut borrower_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut borrower_bytes).await?;
let borrower = Wallet::bytes_to_short_address(&borrower_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid borrower short address bytes",
)
})?;
let borrower = Wallet::bytes_to_short_address(&borrower_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid borrower short address bytes"))?;
// Decode payment schedule and late-payment rules.
let mut payment_period_bytes = vec![0; 1];

View File

@ -31,8 +31,19 @@ pub struct MarketingTransaction {
}
impl MarketingTransaction {
pub const BYTE_LENGTH: usize =
1 + 4 + 8 + 6 + 40 + 100 + 1 + 1 + 2 + 2 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + 8 + Wallet::SIGNATURE_LENGTH;
pub const BYTE_LENGTH: usize = 1
+ 4
+ 8
+ 6
+ 40
+ 100
+ 1
+ 1
+ 2
+ 2
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ 8
+ Wallet::SIGNATURE_LENGTH;
}
impl UnsignedMarketingTransaction {
@ -152,10 +163,7 @@ impl MarketingTransaction {
.write_all(&self.unsigned_marketing.click_value.to_le_bytes())
.await?;
let advertiser_bytes = Wallet::short_address_to_bytes(&self.unsigned_marketing.advertiser)
.ok_or_else(|| {
tokio::io::Error::other("Invalid advertiser short address",
)
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid advertiser short address"))?;
cursor.write_all(&advertiser_bytes).await?;
cursor
.write_all(&self.unsigned_marketing.txfee.to_le_bytes())
@ -168,8 +176,7 @@ impl MarketingTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read the remaining fixed-width marketing bytes.
@ -201,10 +208,8 @@ impl MarketingTransaction {
// Decode advertiser short address, fee, and signature.
let mut advertiser_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut advertiser_bytes).await?;
let advertiser = Wallet::bytes_to_short_address(&advertiser_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid advertiser short address bytes",
)
})?;
let advertiser = Wallet::bytes_to_short_address(&advertiser_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid advertiser short address bytes"))?;
let txfee = cursor.read_u64_le().await?;
let mut signature_bytes = vec![0; Wallet::SIGNATURE_LENGTH];

View File

@ -10,15 +10,15 @@ use crate::{AsyncReadExt, AsyncWriteExt};
#[derive(Debug, Serialize, Clone)] // 255 bytes
pub struct UnsignedCreateNftTransaction {
pub txtype: u8, // 1 byte transaction type, should be 4
pub time: u32, // 4 bytes transaction timestamp
pub creator: String, // 22 bytes creator short address
pub series: u8, // 1 byte 0 for single NFT, 1 for series
pub nft_name: String, // 15 bytes NFT or collection name padded with spaces
pub txtype: u8, // 1 byte transaction type, should be 4
pub time: u32, // 4 bytes transaction timestamp
pub creator: String, // 22 bytes creator short address
pub series: u8, // 1 byte 0 for single NFT, 1 for series
pub nft_name: String, // 15 bytes NFT or collection name padded with spaces
pub item_ipfs: String, // 100 bytes padded CID string
pub count: u32, // 4 bytes 1 for single NFT, otherwise series item count
pub desc: String, // 100 bytes description padded with spaces
pub txfee: u64, // 8 bytes transaction fee
pub count: u32, // 4 bytes 1 for single NFT, otherwise series item count
pub desc: String, // 100 bytes description padded with spaces
pub txfee: u64, // 8 bytes transaction fee
}
#[derive(Debug, Serialize, Clone)] // 921 bytes
@ -28,8 +28,16 @@ pub struct CreateNftTransaction {
}
impl CreateNftTransaction {
pub const BYTE_LENGTH: usize =
1 + 4 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + 1 + 15 + 100 + 4 + 100 + 8 + Wallet::SIGNATURE_LENGTH;
pub const BYTE_LENGTH: usize = 1
+ 4
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ 1
+ 15
+ 100
+ 4
+ 100
+ 8
+ Wallet::SIGNATURE_LENGTH;
}
impl UnsignedCreateNftTransaction {
@ -119,9 +127,7 @@ impl CreateNftTransaction {
.write_all(&self.unsigned_create_nft.time.to_le_bytes())
.await?;
let creator_bytes = Wallet::short_address_to_bytes(&self.unsigned_create_nft.creator)
.ok_or_else(|| {
tokio::io::Error::other("Invalid creator short address")
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid creator short address"))?;
cursor.write_all(&creator_bytes).await?;
cursor
.write_all(&self.unsigned_create_nft.series.to_le_bytes())
@ -149,8 +155,7 @@ impl CreateNftTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read the remaining fixed-width NFT-creation bytes.
@ -161,10 +166,8 @@ impl CreateNftTransaction {
let mut creator_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut creator_bytes).await?;
let creator = Wallet::bytes_to_short_address(&creator_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid creator short address bytes",
)
})?;
let creator = Wallet::bytes_to_short_address(&creator_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid creator short address bytes"))?;
// Decode series flag, name, CID, count, description, fee, and signature.
let series = cursor.read_u8().await?;

View File

@ -65,8 +65,7 @@ impl RewardsTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read the remaining fixed reward bytes.

View File

@ -14,17 +14,17 @@ pub struct UnsignedSwapTransaction {
pub timestamp: u32, // 4 bytes offer creation timestamp
pub offer_expiration: u32, // 4 bytes offer expiration timestamp
pub ticker1: String, // 15 bytes asset offered by sender1
pub nft_series1: u32, // 4 bytes 0 for fungible assets, otherwise NFT series item
pub value1: u64, // 8 bytes amount offered by sender1
pub ticker2: String, // 15 bytes asset offered by sender2
pub nft_series2: u32, // 4 bytes 0 for fungible assets, otherwise NFT series item
pub value2: u64, // 8 bytes amount offered by sender2
pub sender1: String, // 22 bytes sender1 short address
pub sender2: String, // 22 bytes sender2 short address
pub tip1: u64, // 8 bytes miner tip paid in ticker1
pub tip2: u64, // 8 bytes miner tip paid in ticker2
pub txfee1: u64, // 8 bytes sender1 fee
pub txfee2: u64, // 8 bytes sender2 fee
pub nft_series1: u32, // 4 bytes 0 for fungible assets, otherwise NFT series item
pub value1: u64, // 8 bytes amount offered by sender1
pub ticker2: String, // 15 bytes asset offered by sender2
pub nft_series2: u32, // 4 bytes 0 for fungible assets, otherwise NFT series item
pub value2: u64, // 8 bytes amount offered by sender2
pub sender1: String, // 22 bytes sender1 short address
pub sender2: String, // 22 bytes sender2 short address
pub tip1: u64, // 8 bytes miner tip paid in ticker1
pub tip2: u64, // 8 bytes miner tip paid in ticker2
pub txfee1: u64, // 8 bytes sender1 fee
pub txfee2: u64, // 8 bytes sender2 fee
}
#[derive(Debug, Serialize, Clone)] // 1471 bytes
@ -35,9 +35,23 @@ pub struct SwapTransaction {
}
impl SwapTransaction {
pub const BYTE_LENGTH: usize =
1 + 4 + 4 + 15 + 4 + 8 + 15 + 4 + 8 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ 8 + 8 + 8 + 8 + Wallet::SIGNATURE_LENGTH + Wallet::SIGNATURE_LENGTH;
pub const BYTE_LENGTH: usize = 1
+ 4
+ 4
+ 15
+ 4
+ 8
+ 15
+ 4
+ 8
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ 8
+ 8
+ 8
+ 8
+ Wallet::SIGNATURE_LENGTH
+ Wallet::SIGNATURE_LENGTH;
}
impl UnsignedSwapTransaction {
@ -178,13 +192,9 @@ impl SwapTransaction {
.write_all(&self.unsigned_swap.value2.to_le_bytes())
.await?;
let sender1_bytes = Wallet::short_address_to_bytes(&self.unsigned_swap.sender1)
.ok_or_else(|| {
tokio::io::Error::other("Invalid sender1 short address")
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid sender1 short address"))?;
let sender2_bytes = Wallet::short_address_to_bytes(&self.unsigned_swap.sender2)
.ok_or_else(|| {
tokio::io::Error::other("Invalid sender2 short address")
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid sender2 short address"))?;
cursor.write_all(&sender1_bytes).await?;
cursor.write_all(&sender2_bytes).await?;
cursor
@ -208,8 +218,7 @@ impl SwapTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read the remaining fixed-width swap bytes.
@ -238,17 +247,13 @@ impl SwapTransaction {
// Decode both sender short addresses.
let mut sender1_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut sender1_bytes).await?;
let sender1 = Wallet::bytes_to_short_address(&sender1_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid sender1 short address bytes",
)
})?;
let sender1 = Wallet::bytes_to_short_address(&sender1_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid sender1 short address bytes"))?;
let mut sender2_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut sender2_bytes).await?;
let sender2 = Wallet::bytes_to_short_address(&sender2_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid sender2 short address bytes",
)
})?;
let sender2 = Wallet::bytes_to_short_address(&sender2_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid sender2 short address bytes"))?;
// Decode miner tips, fees, and both signatures.
let tip1 = cursor.read_u64_le().await?;

View File

@ -115,9 +115,7 @@ impl CreateTokenTransaction {
.write_all(&self.unsigned_create_token.time.to_le_bytes())
.await?;
let creator_bytes = Wallet::short_address_to_bytes(&self.unsigned_create_token.creator)
.ok_or_else(|| {
tokio::io::Error::other("Invalid creator short address")
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid creator short address"))?;
cursor.write_all(&creator_bytes).await?;
cursor
.write_all(self.unsigned_create_token.ticker.as_bytes())
@ -139,8 +137,7 @@ impl CreateTokenTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read the remaining fixed-width token-creation bytes.
@ -152,10 +149,8 @@ impl CreateTokenTransaction {
// Decode the creator short address.
let mut creator_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut creator_bytes).await?;
let creator = Wallet::bytes_to_short_address(&creator_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid creator short address bytes",
)
})?;
let creator = Wallet::bytes_to_short_address(&creator_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid creator short address bytes"))?;
// Decode the fixed 15-byte token ticker.
let mut ticker_bytes = vec![0; 15];

View File

@ -14,11 +14,11 @@ pub struct UnsignedTransferTransaction {
pub txtype: u8, // 1 byte transaction type, should be 2
pub time: u32, // 4 bytes transaction timestamp
pub value: u64, // 8 bytes number of coins or tokens to send
pub coin: String, // 15 bytes base coin, token ticker, or NFT name, padded with spaces
pub nft_series: u32, // 4 bytes 0 for coins/tokens/1-of-1 NFTs, otherwise NFT series item
pub sender: String, // 22 bytes sender short address
pub coin: String, // 15 bytes base coin, token ticker, or NFT name, padded with spaces
pub nft_series: u32, // 4 bytes 0 for coins/tokens/1-of-1 NFTs, otherwise NFT series item
pub sender: String, // 22 bytes sender short address
pub receiver: String, // 22 bytes receiver short address
pub txfee: u64, // 8 bytes transaction fee
pub txfee: u64, // 8 bytes transaction fee
}
#[derive(Debug, Serialize, Clone)] // 750 bytes
@ -28,8 +28,15 @@ pub struct TransferTransaction {
}
impl TransferTransaction {
pub const BYTE_LENGTH: usize =
1 + 4 + 8 + 15 + 4 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + Wallet::SHORT_ADDRESS_BYTES_LENGTH + 8 + Wallet::SIGNATURE_LENGTH;
pub const BYTE_LENGTH: usize = 1
+ 4
+ 8
+ 15
+ 4
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ 8
+ Wallet::SIGNATURE_LENGTH;
}
impl UnsignedTransferTransaction {
@ -126,14 +133,9 @@ impl TransferTransaction {
.write_all(&self.unsigned_transfer.nft_series.to_le_bytes())
.await?;
let sender_bytes = Wallet::short_address_to_bytes(&self.unsigned_transfer.sender)
.ok_or_else(|| {
tokio::io::Error::other("Invalid sender short address")
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid sender short address"))?;
let receiver_bytes = Wallet::short_address_to_bytes(&self.unsigned_transfer.receiver)
.ok_or_else(|| {
tokio::io::Error::other("Invalid receiver short address",
)
})?;
.ok_or_else(|| tokio::io::Error::other("Invalid receiver short address"))?;
cursor.write_all(&sender_bytes).await?;
cursor.write_all(&receiver_bytes).await?;
cursor
@ -147,8 +149,7 @@ impl TransferTransaction {
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",
));
return Err(tokio::io::Error::other("Invalid Byte Count"));
}
// Read the remaining fixed-width transfer bytes.
@ -168,18 +169,14 @@ impl TransferTransaction {
// Decode the sender short address.
let mut sender_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut sender_bytes).await?;
let sender = Wallet::bytes_to_short_address(&sender_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid sender short address bytes",
)
})?;
let sender = Wallet::bytes_to_short_address(&sender_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid sender short address bytes"))?;
// Decode the receiver short address.
let mut receiver_bytes = vec![0; Wallet::SHORT_ADDRESS_BYTES_LENGTH];
cursor.read_exact(&mut receiver_bytes).await?;
let receiver = Wallet::bytes_to_short_address(&receiver_bytes).ok_or_else(|| {
tokio::io::Error::other("Invalid receiver short address bytes",
)
})?;
let receiver = Wallet::bytes_to_short_address(&receiver_bytes)
.ok_or_else(|| tokio::io::Error::other("Invalid receiver short address bytes"))?;
// Decode fee and signature.
let txfee = cursor.read_u64_le().await?;

View File

@ -9,11 +9,11 @@ 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
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
@ -23,8 +23,12 @@ pub struct VanityAddressTransaction {
}
impl VanityAddressTransaction {
pub const BYTE_LENGTH: usize =
1 + 4 + Wallet::SHORT_ADDRESS_BYTES_LENGTH + Wallet::SHORT_ADDRESS_BYTES_LENGTH + 8 + Wallet::SIGNATURE_LENGTH;
pub const BYTE_LENGTH: usize = 1
+ 4
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ Wallet::SHORT_ADDRESS_BYTES_LENGTH
+ 8
+ Wallet::SIGNATURE_LENGTH;
}
impl UnsignedVanityAddressTransaction {
@ -99,10 +103,9 @@ impl VanityAddressTransaction {
.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")
})?;
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 =
@ -135,15 +138,13 @@ impl VanityAddressTransaction {
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 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 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?;

View File

@ -28,9 +28,7 @@ pub async fn hex_to_u64(hex_string: &str) -> Result<u64, String> {
pub fn binary_to_string(binary_data: Vec<u8>) -> String {
match String::from_utf8(binary_data.clone()) {
Ok(s) => s,
Err(_) => {
"Invalid UTF-8 Data".to_string()
}
Err(_) => "Invalid UTF-8 Data".to_string(),
}
}

View File

@ -1,10 +1,10 @@
use crate::encode;
use crate::ripemd::Digest as RipemdDigest;
use crate::Digest;
use crate::Output;
use crate::Ripemd160;
use crate::Skein256;
use crate::Skein512;
use crate::ripemd::Digest as RipemdDigest;
pub fn skein_128_hash_bytes(data: &[u8]) -> String {
// Contractless 128-bit hashes are Skein256 hashes reduced to 16 bytes.

View File

@ -159,8 +159,7 @@ impl Settings {
config_dir,
),
log_path: Self::expand(
conf.get_from(Some("Paths"), "LOG_PATH")
.unwrap_or("./logs"),
conf.get_from(Some("Paths"), "LOG_PATH").unwrap_or("./logs"),
config_dir,
),
log_level: conf

View File

@ -2,20 +2,22 @@ use crate::blocks::block::{Block, UnminedBlock};
use crate::blocks::genesis::{GenesisTransaction, UnsignedGenesisTransaction};
use crate::common::check_genesis::genesis_checkup;
use crate::common::types::{Transaction, GENESIS_BLOCK_HASH};
use crate::miner::flag::{is_mining_running, is_mining_stop_requested, is_normal_mode, set_mining_state, MiningState};
use crate::log::{error, info};
use crate::miner::flag::{
is_mining_running, is_mining_stop_requested, is_normal_mode, set_mining_state, MiningState,
};
use crate::records::memory::connections::outgoing_connection_count;
use crate::records::memory::response_channels::Command;
use crate::records::record_chain::save::save_block;
use crate::records::record_chain::structs::{SaveBlockParams, SaveType};
use crate::sled::Db;
use crate::sleep;
use crate::verifications::verification_service::VerificationService;
use crate::wallets::structures::Wallet;
use crate::log::{error, info};
use crate::sled::Db;
use crate::Arc;
use crate::Duration;
use crate::Error;
use crate::Mutex;
use crate::sleep;
use crate::Utc;
pub async fn create_genesis_transaction(

View File

@ -1,15 +1,15 @@
use crate::common::check_genesis::genesis_checkup;
use crate::records::memory::response_channels::{reserve_entry, Command};
use crate::sled::Db;
use crate::timeout;
use crate::torrent::structs::Torrent;
use crate::torrent::torrenting_system::torrent_requests::{
handle_response_and_save_torrent, send_request_torrent_message,
};
use crate::sled::Db;
use crate::torrent::structs::Torrent;
use crate::Arc;
use crate::Duration;
use crate::Mutex;
use crate::TcpStream;
use crate::timeout;
pub async fn create_genesis_block(
local_height: u32,

View File

@ -1,15 +1,24 @@
use crate::log::info;
use crate::common::skein::skein_128_hash_bytes;
use crate::log::{info, warn};
use crate::miner::flag::begin_reorg_lock;
use crate::orphans::replay_errors::should_retry_staged_candidate;
use crate::orphans::structs::{OrphanCheckup, UndoTransactions};
use crate::orphans::undo_block_transactions::undo_transactions;
use crate::records::memory::torrent_status::{
get_torrent_status, set_torrent_status, TorrentStatus,
};
use crate::torrent::structs::Torrent;
use crate::records::unpack_block::load_by_binary_data::load_block_from_binary;
use crate::records::unpack_block::unpack_header::load_block_header;
use crate::torrent::structs::{DownloadSave, Torrent};
use crate::torrent::torrenting_system::create_file::combine_pieces;
use crate::torrent::torrenting_system::download_pieces::download_block_pieces;
use crate::torrent::torrenting_system::save_torrent::{
list_staged_torrents_for_height, read_staged_torrent,
};
use crate::torrent::torrenting_system::temp_database_storage::remove_block_pieces_from_db;
use crate::torrent::torrenting_system::torrent_map::create_torrent_map;
use crate::torrent::unpack_local_torrent::load_torrent;
use crate::verifications::verification_service::global_verification_service;
async fn staged_candidates_for_height(height: u32) -> Vec<Torrent> {
let mut candidates = Vec::new();
@ -42,12 +51,12 @@ fn torrent_beats(left: &Torrent, right: &Torrent) -> bool {
.is_lt()
}
async fn best_competing_candidate(
async fn ordered_competing_candidates(
height: u32,
local_torrent: &Torrent,
candidates: &[Torrent],
) -> Option<Torrent> {
let mut preferred: Option<Torrent> = None;
) -> Vec<Torrent> {
let mut ordered = Vec::new();
for torrent in candidates {
// Identical info hashes are the same block candidate, not a competing fork.
@ -64,19 +73,83 @@ async fn best_competing_candidate(
continue;
}
match &preferred {
Some(current_torrent) => {
// Among candidates that have not been ruled out, choose the
// same deterministic winner the block fight uses.
if torrent_beats(torrent, current_torrent) {
preferred = Some(torrent.clone());
}
}
None => preferred = Some(torrent.clone()),
ordered.push(torrent.clone());
}
// Among candidates that have not been ruled out, choose the same
// deterministic order the block fight uses.
ordered.sort_by(|a, b| {
a.info
.timestamp
.cmp(&b.info.timestamp)
.then(a.info.nonce.cmp(&b.info.nonce))
.then(a.info.vrf.cmp(&b.info.vrf))
});
ordered
}
async fn cleanup_candidate_pieces(db: &crate::sled::Db, height: u32, torrent: &Torrent) {
let _ = remove_block_pieces_from_db(db, height, &torrent.info.info_hash).await;
}
async fn candidate_attaches_before_rollback(
params: &OrphanCheckup,
height: u32,
torrent: &Torrent,
wallet_key: &str,
) -> Result<(), String> {
// Metadata may choose a candidate, but only downloaded block bytes can
// prove the rollback is safe.
torrent.verify(height, &params.db, wallet_key).await?;
let verification_service = global_verification_service()
.ok_or_else(|| "Verification service not initialized".to_string())?;
let torrent_map = create_torrent_map(torrent).await?;
let download_save_params = DownloadSave {
torrent_map,
torrent: torrent.clone(),
staged_path: String::new(),
block_number: height,
allow_during_reorg: true,
allow_historical: true,
db: params.db.clone(),
verification_service: std::sync::Arc::new(verification_service),
map: params.map.clone(),
};
download_block_pieces(download_save_params).await?;
let result = combine_pieces(&params.db, height, &torrent.info.info_hash).await?;
if result.len() != torrent.info.length as usize {
cleanup_candidate_pieces(&params.db, height, torrent).await;
return Err("Downloaded candidate length does not match torrent metadata.".to_string());
}
if skein_128_hash_bytes(&result) != torrent.info.info_hash {
cleanup_candidate_pieces(&params.db, height, torrent).await;
return Err("Hash validation failed for complete block".to_string());
}
let loaded_block = load_block_from_binary(&result)
.await
.map_err(|err| format!("Failed to load block from binary: {err}"))?;
let header_hash = loaded_block.vrf_block.hash().await;
if header_hash != torrent.info.block_hash {
cleanup_candidate_pieces(&params.db, height, torrent).await;
return Err("Candidate header hash does not match torrent metadata.".to_string());
}
if height > 0 {
let parent_height = height - 1;
let parent_header = load_block_header(parent_height).await?;
let parent_hash = parent_header.hash().await;
if loaded_block.vrf_block.unmined_block.previous_hash != parent_hash {
cleanup_candidate_pieces(&params.db, height, torrent).await;
return Err("Incorrect previous_block_hash.".to_string());
}
}
preferred
Ok(())
}
pub async fn checkup(params: OrphanCheckup, wallet_key: &str) -> Result<(), String> {
@ -89,41 +162,69 @@ pub async fn checkup(params: OrphanCheckup, wallet_key: &str) -> Result<(), Stri
let local_torrent = load_torrent(&params.db, height).await?;
let staged_candidates = staged_candidates_for_height(height).await;
if let Some(competing_torrent) =
best_competing_candidate(height, &local_torrent, &staged_candidates).await
{
let competing_info_hash = competing_torrent.info.info_hash.clone();
// If the best staged torrent wins this height, rollback starts
// here and replay rebuilds forward from staged candidates.
let undo_transactions_params = UndoTransactions {
start_height: height,
db: params.db.clone(),
stream: params.stream.clone(),
map: params.map.clone(),
node_syncing: params.node_syncing,
connections_key: params.connections_key.clone(),
};
let ordered_candidates =
ordered_competing_candidates(height, &local_torrent, &staged_candidates).await;
if torrent_beats(&competing_torrent, &local_torrent) {
set_torrent_status(height, &competing_info_hash, TorrentStatus::Valid).await;
if !params.node_syncing {
begin_reorg_lock().await;
for competing_torrent in ordered_candidates {
let competing_info_hash = competing_torrent.info.info_hash.clone();
if !torrent_beats(&competing_torrent, &local_torrent) {
// The local block remains the winner at this height. Since
// candidates are sorted best-first, every remaining staged
// competitor has also lost to the local block.
for staged_torrent in &staged_candidates {
if staged_torrent.info.info_hash != local_torrent.info.info_hash {
set_torrent_status(
height,
&staged_torrent.info.info_hash,
TorrentStatus::Invalid,
)
.await;
}
}
info!("[orphan] adopting competing staged chain from height {height}");
undo_transactions(undo_transactions_params, wallet_key).await?;
return Ok(());
break;
}
// The local block remains the winner at this height, so every
// staged competitor for the same height has now been checked.
for staged_torrent in staged_candidates {
if staged_torrent.info.info_hash != local_torrent.info.info_hash {
set_torrent_status(
height,
&staged_torrent.info.info_hash,
TorrentStatus::Invalid,
)
.await;
match candidate_attaches_before_rollback(
&params,
height,
&competing_torrent,
wallet_key,
)
.await
{
Ok(()) => {
let undo_transactions_params = UndoTransactions {
start_height: height,
db: params.db.clone(),
stream: params.stream.clone(),
map: params.map.clone(),
node_syncing: params.node_syncing,
connections_key: params.connections_key.clone(),
};
set_torrent_status(height, &competing_info_hash, TorrentStatus::Valid).await;
if !params.node_syncing {
begin_reorg_lock().await;
}
info!("[orphan] adopting proven staged chain from height {height}");
undo_transactions(undo_transactions_params, wallet_key).await?;
return Ok(());
}
Err(err) => {
let status = if should_retry_staged_candidate(&err) {
TorrentStatus::Pending
} else {
TorrentStatus::Invalid
};
set_torrent_status(height, &competing_info_hash, status).await;
warn!(
"[orphan] staged candidate failed pre-rollback proof: height={height} err={err}"
);
if status == TorrentStatus::Pending {
break;
}
}
}
}

View File

@ -5,8 +5,10 @@ pub fn should_retry_staged_candidate(error: &str) -> bool {
|| error.contains("piece not found")
|| error.contains("Requested candidate not found")
|| error.contains("Block not found")
|| (error.contains("Block ") && error.contains(" not found"))
|| error.contains("Timed out waiting for piece")
|| error.contains("Timed out waiting for replacement torrent")
|| error.contains("No replacement torrent received")
|| error.contains("Piece reply channel closed")
|| error.contains("Replay waiting for block pieces")
}

View File

@ -1,5 +1,6 @@
use crate::orphans::structs::UndoTransactions;
use crate::orphans::replay_errors::should_retry_staged_candidate;
use crate::orphans::structs::UndoTransactions;
use crate::orphans::torrent_candidates::hydrate_torrent_candidates;
use crate::records::block_height::get_block_height::get_height;
use crate::records::memory::response_channels::reserve_entry;
use crate::records::memory::torrent_status::{
@ -13,6 +14,7 @@ use crate::torrent::torrenting_system::torrent_requests::{
handle_response_and_save_torrent, send_request_torrent_message,
};
use crate::{timeout, Duration};
use std::collections::HashSet;
pub async fn save_new_blocks(
params: &UndoTransactions,
@ -22,6 +24,7 @@ pub async fn save_new_blocks(
) -> Result<(), String> {
// after rollback, request and save each remote block from the
// divergence point up to the height we need to restore
let mut hydrated_heights = HashSet::new();
loop {
let mut resolved_from_staging = false;
let staged_candidates = list_staged_torrents_for_height(true_start_height).await?;
@ -101,12 +104,7 @@ pub async fn save_new_blocks(
} else {
TorrentStatus::Invalid
};
set_torrent_status(
true_start_height,
&torrent_info_hash,
status,
)
.await;
set_torrent_status(true_start_height, &torrent_info_hash, status).await;
}
}
}
@ -124,6 +122,22 @@ pub async fn save_new_blocks(
}
}
if hydrated_heights.insert(true_start_height) {
let imported = hydrate_torrent_candidates(
params.stream.clone(),
params.map.clone(),
params.connections_key.clone(),
)
.await?;
if imported > 0 {
// Peer candidate hydration can add staged torrents that
// are not canonical on the peer yet. Restart this height
// so those candidates are tried before canonical fallback.
continue;
}
}
// No staged candidate worked, so request the replacement torrent
// directly from the connected peer.
let (hashmap_key, _save_tx, save_rx) = reserve_entry(params.map.clone()).await;

View File

@ -47,11 +47,8 @@ pub async fn update_snapshot(db: &Db, current_height: u32) -> Result<(), String>
let hash = header.hash().await;
let value = format!("{snapshot_height}:{hash}");
let key = b"snapshot";
db.insert(key, value.as_bytes()).map_err(|e| {
format!(
"Failed to store snapshot at height {snapshot_height}: {e}"
)
})?;
db.insert(key, value.as_bytes())
.map_err(|e| format!("Failed to store snapshot at height {snapshot_height}: {e}"))?;
Ok(())
}
@ -97,7 +94,9 @@ pub async fn snapshot_verified(params: UndoTransactions, wallet_key: &str) -> bo
}
}
_ => {
error!("Unable to verify remote snapshot torrent at height {snap_height}");
error!(
"Unable to verify remote snapshot torrent at height {snap_height}"
);
return true;
}
}

View File

@ -104,6 +104,7 @@ async fn replay_staged_torrents(params: &OrphanCheckup2, wallet_key: &str) -> Re
}
let mut advanced_height = false;
let mut retryable_pending = false;
for torrent in ordered_candidates {
let torrent_info_hash = torrent.info.info_hash.clone();
@ -137,6 +138,7 @@ async fn replay_staged_torrents(params: &OrphanCheckup2, wallet_key: &str) -> Re
}
Err(err) => {
if should_retry_staged_candidate(&err) {
retryable_pending = true;
// Piece availability is not proof that the candidate
// lost the block fight; leave it pending so a later
// orphan pass can retry after more peers stage it.
@ -163,6 +165,11 @@ async fn replay_staged_torrents(params: &OrphanCheckup2, wallet_key: &str) -> Re
if !advanced_height {
// Every staged candidate for the current expected height was
// exhausted without extending the chain, so stop replay here.
if retryable_pending {
return Err(format!(
"Replay waiting for block pieces at height {expected_height}"
));
}
return Ok(());
}
}
@ -196,9 +203,16 @@ pub async fn sync_checkup(params: OrphanCheckup2, wallet_key: &str) -> Result<()
if !snapshot_verified(undo_transactions_params, wallet_key).await {
// A snapshot rollback already happened, so replay staged torrents and
// exit instead of running the near-tip rules against stale heights.
let mut replay_waiting = false;
match replay_staged_torrents(&params, wallet_key).await {
Ok(()) => {}
Err(err) => error!("[orphan] staged torrent replay error: {err}"),
Err(err) => {
replay_waiting = should_retry_staged_candidate(&err);
error!("[orphan] staged torrent replay error: {err}");
}
}
if replay_waiting && !params.node_syncing {
return Err("orphan replay is waiting for block data".to_string());
}
if !params.node_syncing {
end_reorg_lock();
@ -218,14 +232,33 @@ pub async fn sync_checkup(params: OrphanCheckup2, wallet_key: &str) -> Result<()
};
deep_sync_rollback(checkup_params.clone(), wallet_key).await;
let mut replay_waiting = false;
let height_before_window_check = get_height(&params.db);
match orphan_window_check(checkup_params, wallet_key).await {
Ok(()) => {}
Err(err) => error!("[orphan] orphan window check error: {err}"),
Err(err) => {
if should_retry_staged_candidate(&err)
&& get_height(&params.db) < height_before_window_check
{
replay_waiting = true;
}
error!("[orphan] orphan window check error: {err}");
}
}
let height_before_replay = get_height(&params.db);
match replay_staged_torrents(&params, wallet_key).await {
Ok(()) => {}
Err(err) => error!("[orphan] staged torrent replay error: {err}"),
Err(err) => {
replay_waiting |= should_retry_staged_candidate(&err);
error!("[orphan] staged torrent replay error: {err}");
}
}
if get_height(&params.db) > height_before_replay {
replay_waiting = false;
}
if replay_waiting && !params.node_syncing {
return Err("orphan replay is waiting for block data".to_string());
}
if !params.node_syncing {
end_reorg_lock();

View File

@ -39,10 +39,17 @@ pub async fn undo_transactions(params: UndoTransactions, wallet_key: &str) -> Re
for transaction in transactions.into_iter().rev() {
match transaction {
Transaction::Rewards(rewards_tx) => {
undo_rewards_transaction(rewards_tx, &mining_receiver, &params.db, current_height).await
undo_rewards_transaction(
rewards_tx,
&mining_receiver,
&params.db,
current_height,
)
.await
}
Transaction::Transfer(transfer_tx) => {
undo_transfer_transaction(transfer_tx.clone(), &mining_receiver, &params.db).await;
undo_transfer_transaction(transfer_tx.clone(), &mining_receiver, &params.db)
.await;
rolled_back_transactions.push(Transaction::Transfer(transfer_tx));
}
Transaction::Burn(burn_tx) => {
@ -50,20 +57,35 @@ pub async fn undo_transactions(params: UndoTransactions, wallet_key: &str) -> Re
rolled_back_transactions.push(Transaction::Burn(burn_tx));
}
Transaction::Token(create_token_tx) => {
undo_create_token_transaction(create_token_tx.clone(), &mining_receiver, &params.db)
.await;
undo_create_token_transaction(
create_token_tx.clone(),
&mining_receiver,
&params.db,
)
.await;
rolled_back_transactions.push(Transaction::Token(create_token_tx));
}
Transaction::IssueToken(issue_token_tx) => {
undo_issue_token_transaction(issue_token_tx.clone(), &mining_receiver, &params.db).await;
undo_issue_token_transaction(
issue_token_tx.clone(),
&mining_receiver,
&params.db,
)
.await;
rolled_back_transactions.push(Transaction::IssueToken(issue_token_tx));
}
Transaction::Nft(create_nft_tx) => {
undo_create_nft_transaction(create_nft_tx.clone(), &mining_receiver, &params.db).await;
undo_create_nft_transaction(
create_nft_tx.clone(),
&mining_receiver,
&params.db,
)
.await;
rolled_back_transactions.push(Transaction::Nft(create_nft_tx));
}
Transaction::Marketing(marketing_tx) => {
undo_marketing_transaction(marketing_tx.clone(), &mining_receiver, &params.db).await;
undo_marketing_transaction(marketing_tx.clone(), &mining_receiver, &params.db)
.await;
rolled_back_transactions.push(Transaction::Marketing(marketing_tx));
}
Transaction::Swap(swap_tx) => {
@ -71,15 +93,22 @@ pub async fn undo_transactions(params: UndoTransactions, wallet_key: &str) -> Re
rolled_back_transactions.push(Transaction::Swap(swap_tx));
}
Transaction::Lender(loan_tx) => {
undo_loan_creation_transaction(loan_tx.clone(), &mining_receiver, &params.db).await;
undo_loan_creation_transaction(loan_tx.clone(), &mining_receiver, &params.db)
.await;
rolled_back_transactions.push(Transaction::Lender(loan_tx));
}
Transaction::Borrower(borrower_tx) => {
undo_borrower_transaction(borrower_tx.clone(), &mining_receiver, &params.db).await?;
undo_borrower_transaction(borrower_tx.clone(), &mining_receiver, &params.db)
.await?;
rolled_back_transactions.push(Transaction::Borrower(borrower_tx));
}
Transaction::Collateral(collateral_tx) => {
undo_collateral_transaction(collateral_tx.clone(), &mining_receiver, &params.db).await?;
undo_collateral_transaction(
collateral_tx.clone(),
&mining_receiver,
&params.db,
)
.await?;
rolled_back_transactions.push(Transaction::Collateral(collateral_tx));
}
Transaction::Genesis(_) => {
@ -87,10 +116,11 @@ pub async fn undo_transactions(params: UndoTransactions, wallet_key: &str) -> Re
// the requested rollback boundary is invalid.
return Err(
"Genesis transaction cannot be undone by orphan rollback".to_string()
)
);
}
Transaction::Vanity(vanity_tx) => {
undo_vanity_transaction(vanity_tx.clone(), &mining_receiver, &params.db).await?;
undo_vanity_transaction(vanity_tx.clone(), &mining_receiver, &params.db)
.await?;
rolled_back_transactions.push(Transaction::Vanity(vanity_tx));
}
}

View File

@ -11,12 +11,12 @@ use crate::blocks::transfer::TransferTransaction;
use crate::blocks::vanity::VanityAddressTransaction;
use crate::common::nft_assets::nft_asset_name;
use crate::common::types::Transaction;
use crate::decode;
use crate::records::memory::mempool::{restore_processed_by_signatures, BASECOIN};
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse;
use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::decode;
async fn restore_if_spendable<F, Fut>(signatures: &[String], spendable: bool, insert: F)
where
@ -107,8 +107,14 @@ pub async fn restore_create_nft(transaction: &CreateNftTransaction, db: &Db) {
pub async fn restore_marketing(transaction: &MarketingTransaction, db: &Db) {
let marketing = &transaction.unsigned_marketing;
let spendable =
balance_checkup(db, 0, marketing.txfee, BASECOIN.clone(), &marketing.advertiser).await;
let spendable = balance_checkup(
db,
0,
marketing.txfee,
BASECOIN.clone(),
&marketing.advertiser,
)
.await;
let signature = transaction.signature.clone();
restore_if_spendable(&[signature], spendable, || async {
@ -123,31 +129,53 @@ pub async fn restore_swap(transaction: &SwapTransaction, db: &Db) {
let asset2 = nft_asset_name(&swap.ticker2, swap.nft_series2);
let value1 = swap.value1.saturating_add(swap.tip1);
let value2 = swap.value2.saturating_add(swap.tip2);
let sender1_spendable =
balance_checkup(db, value1, swap.txfee1, asset1, &swap.sender1).await;
let sender2_spendable =
balance_checkup(db, value2, swap.txfee2, asset2, &swap.sender2).await;
let signatures = vec![transaction.signature1.clone(), transaction.signature2.clone()];
let sender1_spendable = balance_checkup(db, value1, swap.txfee1, asset1, &swap.sender1).await;
let sender2_spendable = balance_checkup(db, value2, swap.txfee2, asset2, &swap.sender2).await;
let signatures = vec![
transaction.signature1.clone(),
transaction.signature2.clone(),
];
restore_if_spendable(&signatures, sender1_spendable && sender2_spendable, || async {
let _ = transaction.add_to_memory().await;
})
restore_if_spendable(
&signatures,
sender1_spendable && sender2_spendable,
|| async {
let _ = transaction.add_to_memory().await;
},
)
.await;
}
pub async fn restore_loan_creation(transaction: &LoanContractTransaction, db: &Db) {
let loan = &transaction.unsigned_loan_contract;
let lender_spendable =
balance_checkup(db, loan.loan_amount, loan.txfee, loan.loan_coin.clone(), &loan.lender)
.await;
let borrower_spendable =
balance_checkup(db, loan.collateral_amount, 0, loan.collateral.clone(), &loan.borrower)
.await;
let signatures = vec![transaction.signature1.clone(), transaction.signature2.clone()];
let lender_spendable = balance_checkup(
db,
loan.loan_amount,
loan.txfee,
loan.loan_coin.clone(),
&loan.lender,
)
.await;
let borrower_spendable = balance_checkup(
db,
loan.collateral_amount,
0,
loan.collateral.clone(),
&loan.borrower,
)
.await;
let signatures = vec![
transaction.signature1.clone(),
transaction.signature2.clone(),
];
restore_if_spendable(&signatures, lender_spendable && borrower_spendable, || async {
let _ = transaction.add_to_memory().await;
})
restore_if_spendable(
&signatures,
lender_spendable && borrower_spendable,
|| async {
let _ = transaction.add_to_memory().await;
},
)
.await;
}
@ -168,8 +196,14 @@ pub async fn restore_borrower(transaction: &ContractPaymentTransaction, db: &Db)
pub async fn restore_collateral(transaction: &CollateralClaimTransaction, db: &Db) {
let collateral = &transaction.unsigned_collateral_claim;
let spendable =
balance_checkup(db, 0, collateral.txfee, BASECOIN.clone(), &collateral.address).await;
let spendable = balance_checkup(
db,
0,
collateral.txfee,
BASECOIN.clone(),
&collateral.address,
)
.await;
let signature = transaction.signature.clone();
restore_if_spendable(&[signature], spendable, || async {

View File

@ -89,5 +89,4 @@ pub async fn undo_burn_transaction(transaction: BurnTransaction, mining_receiver
&restored_supply.to_le_bytes(),
);
}
}

View File

@ -77,5 +77,4 @@ pub async fn undo_create_nft_transaction(
let _ = remove_nft_origin(db, nft_name);
tree.remove(nft_name.as_bytes()).unwrap();
}
}

View File

@ -65,5 +65,4 @@ pub async fn undo_create_token_transaction(
limit_tree.remove(key).unwrap();
// Token history is cleared because the token itself no longer exists.
let _ = clear_token_history(db, ticker);
}

View File

@ -58,5 +58,4 @@ pub async fn undo_issue_token_transaction(
let restored_supply = current_supply.saturating_sub(*number);
let _ = token_tree.insert(ticker.as_bytes(), &restored_supply.to_le_bytes());
}
}

View File

@ -89,5 +89,4 @@ pub async fn undo_loan_creation_transaction(
if nft_tree.contains_key(loan_coin.as_bytes()).unwrap_or(false) {
let _ = remove_nft_history_entry(db, loan_coin, &hash_binary);
}
}

View File

@ -45,5 +45,4 @@ pub async fn undo_marketing_transaction(
let tree = db.open_tree("txid").unwrap();
let key = hash;
tree.remove(key).unwrap();
}

View File

@ -2,7 +2,9 @@ use crate::blocks::rewards::RewardsTransaction;
use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::decode;
use crate::records::balance_sheet::operations::balance_sheet_operation_with_db;
use crate::records::record_chain::rewards_tx::{remove_reward_credit_marker, reward_credit_applied};
use crate::records::record_chain::rewards_tx::{
remove_reward_credit_marker, reward_credit_applied,
};
use crate::sled::Db;
pub async fn undo_rewards_transaction(

View File

@ -99,5 +99,4 @@ pub async fn undo_swap_transaction(transaction: SwapTransaction, mining_receiver
if nft_tree.contains_key(asset2.as_bytes()).unwrap_or(false) {
let _ = remove_nft_history_entry(db, &asset2, &hash);
}
}

View File

@ -67,5 +67,4 @@ pub async fn undo_transfer_transaction(
{
let _ = remove_nft_history_entry(db, &transfer_asset, &hash);
}
}

View File

@ -46,11 +46,7 @@ pub async fn get_balance_with_db(
// Balance queries accept vanity/registered aliases, but storage always
// resolves back to the canonical short address.
let canonical_address = resolve_canonical_registered_short_address(db, address)
.map_err(|err| {
io::Error::other(
format!("Wallet registry lookup failed: {err}"),
)
})?
.map_err(|err| io::Error::other(format!("Wallet registry lookup failed: {err}")))?
.unwrap_or_else(|| address.to_string());
get_balance(&canonical_address, coin_type).await

View File

@ -69,9 +69,7 @@ pub fn balance_sheet_operation(
let mut file_balance = if file_exists {
// Existing balances are stored as a single little-endian u64.
file.read_exact(&mut buffer).map_err(|e| {
eprintln!(
"Error reading file balance_sheet address {address}: {e}"
);
eprintln!("Error reading file balance_sheet address {address}: {e}");
e
})?;
u64::from_le_bytes(buffer)
@ -142,11 +140,7 @@ pub fn balance_sheet_operation_with_db(
// Vanity or alternate registered addresses resolve to the canonical short
// address before the filesystem balance is updated.
let canonical_address = resolve_canonical_registered_short_address(db, address)
.map_err(|err| {
io::Error::other(
format!("Wallet registry lookup failed: {err}"),
)
})?
.map_err(|err| io::Error::other(format!("Wallet registry lookup failed: {err}")))?
.unwrap_or_else(|| address.to_string());
balance_sheet_operation(&canonical_address, balance, coin_type, operand)

View File

@ -62,11 +62,7 @@ fn finish_reconnect() {
RECONNECT_IN_PROGRESS.store(false, AtomicOrdering::SeqCst);
}
pub async fn set_reconnect_context(
db: Db,
wallet_key: String,
map: Arc<Mutex<Command>>,
) {
pub async fn set_reconnect_context(db: Db, wallet_key: String, map: Arc<Mutex<Command>>) {
let mut context = RECONNECT_CONTEXT.lock().await;
// Store enough state for later liveness checks to reconnect without
// needing the original startup stack.
@ -415,9 +411,7 @@ impl Connection {
};
self.connection_map
.get(&connection_key)
.filter(|info| {
ClientType::from_bytes(&info.client_type) == Some(ClientType::Miner)
})
.filter(|info| ClientType::from_bytes(&info.client_type) == Some(ClientType::Miner))
.map(|info| Arc::clone(&info.stream))
}
@ -430,16 +424,18 @@ impl Connection {
let conn = lock.as_ref()?;
let ip_bytes = ip_to_binary(&ip);
conn.connection_map.iter().find_map(|(connection_key, info)| {
if connection_key.ip == ip_bytes
&& connection_key.port == port
&& ClientType::from_bytes(&info.client_type) == Some(ClientType::Miner)
{
Some(Arc::clone(&info.stream))
} else {
None
}
})
conn.connection_map
.iter()
.find_map(|(connection_key, info)| {
if connection_key.ip == ip_bytes
&& connection_key.port == port
&& ClientType::from_bytes(&info.client_type) == Some(ClientType::Miner)
{
Some(Arc::clone(&info.stream))
} else {
None
}
})
}
// Build the serialized connection key for a live stream when only

View File

@ -380,8 +380,7 @@ pub async fn get_basecoin_balance(
.await?;
let total: i64 = row.get(0);
let chain_loan_payments =
pending_saved_loan_payment_balance(db, &addresses, &BASECOIN).await?;
let chain_loan_payments = pending_saved_loan_payment_balance(db, &addresses, &BASECOIN).await?;
Ok((total.max(0) as u64).saturating_add(chain_loan_payments))
}
@ -452,4 +451,3 @@ pub async fn total_transactions() -> RpcResponse {
RpcResponse::Binary(result)
}

View File

@ -6,9 +6,7 @@ use crate::config::SETTINGS;
use crate::decode;
use crate::lazy_static;
use crate::records::memory::structs::BalanceKey;
use crate::records::wallet_registry::{
resolve_canonical_registered_short_address,
};
use crate::records::wallet_registry::resolve_canonical_registered_short_address;
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse;
use crate::sled::Db;
@ -202,7 +200,6 @@ impl SelectedMempoolBatch {
}
}
mod lookups;
mod processing;
mod schema;
@ -213,9 +210,9 @@ pub use lookups::{
signature_exists, total_transactions, transaction_by_signature, transactions_by_address,
};
pub use processing::{
mark_processed_by_signatures, mark_selected_transactions_processed,
delete_by_signatures, mark_processed_by_signatures, mark_selected_transactions_processed,
restore_processed_by_signatures, restore_selected_transactions_processed,
spawn_processed_cleanup, delete_by_signatures,
spawn_processed_cleanup,
};
pub use schema::{clear_mempool, init_db, setup_mempool};
pub use selection::{
@ -320,9 +317,8 @@ async fn mark_rows_by_ids(
return Ok(());
}
let statement = format!(
"UPDATE {table} SET processed=true, processed_block_number=$1 WHERE id = ANY($2)"
);
let statement =
format!("UPDATE {table} SET processed=true, processed_block_number=$1 WHERE id = ANY($2)");
client.execute(&statement, &[&block_number, &ids]).await?;
Ok(())
}
@ -332,8 +328,9 @@ async fn unmark_rows_by_ids(client: &Client, table: &str, ids: &[i64]) -> Result
return Ok(());
}
let statement =
format!("UPDATE {table} SET processed=false, processed_block_number=NULL WHERE id = ANY($1)");
let statement = format!(
"UPDATE {table} SET processed=false, processed_block_number=NULL WHERE id = ANY($1)"
);
client.execute(&statement, &[&ids]).await?;
Ok(())
}

View File

@ -63,12 +63,7 @@ pub async fn restore_selected_transactions_processed(batch: &SelectedMempoolBatc
unmark_rows_by_ids(client, "transfer", &ids_for_table(batch, "transfer")).await?;
unmark_rows_by_ids(client, "token", &ids_for_table(batch, "token")).await?;
unmark_rows_by_ids(
client,
"issue_token",
&ids_for_table(batch, "issue_token"),
)
.await?;
unmark_rows_by_ids(client, "issue_token", &ids_for_table(batch, "issue_token")).await?;
unmark_rows_by_ids(client, "burn", &ids_for_table(batch, "burn")).await?;
unmark_rows_by_ids(client, "nft", &ids_for_table(batch, "nft")).await?;
unmark_rows_by_ids(client, "marketing", &ids_for_table(batch, "marketing")).await?;
@ -162,7 +157,6 @@ pub fn spawn_processed_cleanup(saved_block_number: u32) {
});
}
pub async fn mark_processed_by_signatures(signatures: &[String], block_number: u32) -> Result<()> {
// Synced blocks arrive with signatures instead of selected-row IDs,
// so processed marking on the updating path works by signature.
@ -331,4 +325,3 @@ pub async fn delete_by_signatures(signatures: &[String]) -> Result<()> {
Ok(())
}

View File

@ -395,4 +395,3 @@ pub async fn clear_mempool() -> Result<()> {
Ok(())
}

View File

@ -403,7 +403,11 @@ pub async fn apply_selected_transaction_math(
);
let number_bytes = (*number as u64).to_le_bytes();
pending_effects.set_tree("tokens", ticker.as_bytes().to_vec(), number_bytes.to_vec());
pending_effects.set_tree(
"tokens",
ticker.as_bytes().to_vec(),
number_bytes.to_vec(),
);
// Local mined blocks need to persist the token hard-limit
// metadata just like the downloaded-block save path does.
@ -417,13 +421,21 @@ pub async fn apply_selected_transaction_math(
let hard_limit = token_row
.and_then(|row| row.try_get::<_, Option<i16>>("hard_limit").ok().flatten())
.unwrap_or(1) as u8;
pending_effects.set_tree("token_limits", ticker.as_bytes().to_vec(), vec![hard_limit]);
pending_effects.set_tree(
"token_limits",
ticker.as_bytes().to_vec(),
vec![hard_limit],
);
pending_effects.set_tree(
"token_origins",
ticker.as_bytes().to_vec(),
hash.as_bytes().to_vec(),
);
pending_effects.append_tree("token_history", ticker.as_bytes().to_vec(), decode(hash)?);
pending_effects.append_tree(
"token_history",
ticker.as_bytes().to_vec(),
decode(hash)?,
);
pending_effects.set_tree(
"txid",
decode(hash)?,
@ -450,7 +462,11 @@ pub async fn apply_selected_transaction_math(
// Additional issuance increases the live fungible supply without
// changing the original token metadata entries.
pending_effects.add_token_supply(ticker, *number as u64);
pending_effects.append_tree("token_history", ticker.as_bytes().to_vec(), decode(hash)?);
pending_effects.append_tree(
"token_history",
ticker.as_bytes().to_vec(),
decode(hash)?,
);
pending_effects.set_tree(
"txid",
decode(hash)?,
@ -527,8 +543,16 @@ pub async fn apply_selected_transaction_math(
add_balance_change(db, &mut balance_changes, creator, nft_name, NFT_UNIT);
pending_effects.set_tree("nfts", nft_name.as_bytes().to_vec(), b"1".to_vec());
pending_effects.set_tree("nft_origins", nft_name.as_bytes().to_vec(), decode(hash)?);
pending_effects.append_tree("nft_history", nft_name.as_bytes().to_vec(), decode(hash)?);
pending_effects.set_tree(
"nft_origins",
nft_name.as_bytes().to_vec(),
decode(hash)?,
);
pending_effects.append_tree(
"nft_history",
nft_name.as_bytes().to_vec(),
decode(hash)?,
);
}
add_balance_change(db, &mut balance_changes, creator, &BASECOIN, -*fee);
add_balance_change_bytes(
@ -764,7 +788,8 @@ pub async fn apply_selected_transaction_math(
*tip,
);
pending_effects.append_contract_payment(decode(contract_hash)?, *payback_amount as u64);
pending_effects
.append_contract_payment(decode(contract_hash)?, *payback_amount as u64);
let loan_coin_name = binary_to_string(loan_coin.clone());
let nft_tree = db.open_tree("nfts")?;
if nft_tree.contains_key(loan_coin_name.as_bytes())? {
@ -927,4 +952,3 @@ pub async fn delete_selected_transactions(batch: &SelectedMempoolBatch) -> Resul
Ok(())
}

View File

@ -3,9 +3,9 @@
pub mod averages;
pub mod connections;
pub mod enums;
pub mod response_channels;
pub mod mempool;
pub mod network_mapping;
pub mod response_channels;
pub mod structs;
pub mod torrent_status;
pub mod torrentmap;

View File

@ -259,5 +259,4 @@ impl NodeInfo {
RpcResponse::Binary(b"Success".to_vec())
}
}

View File

@ -297,5 +297,4 @@ impl NodeInfo {
RpcResponse::Binary(b"Success: Node marked as deleted".to_vec())
}
}

View File

@ -73,5 +73,4 @@ impl NodeInfo {
}
Ok(())
}
}

View File

@ -10,11 +10,11 @@ use crate::records::ip_score::enums::InfractionType;
use crate::records::ip_score::score::update_ip_score;
use crate::records::memory::connections::{spawn_reconnect_bootstrap, CONNECTIONS};
use crate::records::memory::enums::ConnectionType;
use crate::records::memory::response_channels::{reserve_entry, Command};
use crate::records::memory::network_mapping::enums::NodeEditType;
use crate::records::memory::network_mapping::structs::{
AddAddressParams, DeleteAddressParams, PingMonitorParams, SignedNodeEdit, NODE_RECORD_BYTES,
};
use crate::records::memory::response_channels::{reserve_entry, Command};
use crate::records::memory::structs::Connection;
use crate::records::unpack_block::unpack_header::load_block_header;
use crate::rpc::client::handshake_processing::BootstrapParams;
@ -88,4 +88,3 @@ pub mod enums;
mod mined_counts;
mod queries;
pub mod structs;

View File

@ -11,7 +11,6 @@ impl NodeInfo {
false
}
pub async fn find_address_by_ip(ip: &str) -> Option<String> {
let map = ADDRESS_MAP.lock().await;
for (address, node_info) in map.iter() {

View File

@ -80,13 +80,21 @@ pub async fn process_lender(
pending_effects.append_tree_if_key_exists(
"nfts",
"nft_history",
transaction.unsigned_loan_contract.collateral.as_bytes().to_vec(),
transaction
.unsigned_loan_contract
.collateral
.as_bytes()
.to_vec(),
txhash_bytes.clone(),
);
pending_effects.append_tree_if_key_exists(
"nfts",
"nft_history",
transaction.unsigned_loan_contract.loan_coin.as_bytes().to_vec(),
transaction
.unsigned_loan_contract
.loan_coin
.as_bytes()
.to_vec(),
txhash_bytes,
);
Ok(binary_data)

View File

@ -60,8 +60,16 @@ pub async fn process_nft(
BalanceOperand::Addition,
);
pending_effects.set_tree("nfts", nft_save_name.as_bytes().to_vec(), b"1".to_vec());
pending_effects.set_tree("nft_origins", nft_save_name.as_bytes().to_vec(), txhash_bytes.clone());
pending_effects.append_tree("nft_history", nft_save_name.into_bytes(), txhash_bytes.clone());
pending_effects.set_tree(
"nft_origins",
nft_save_name.as_bytes().to_vec(),
txhash_bytes.clone(),
);
pending_effects.append_tree(
"nft_history",
nft_save_name.into_bytes(),
txhash_bytes.clone(),
);
}
} else {
let nft_save_name = transaction.unsigned_create_nft.nft_name.clone();
@ -72,8 +80,16 @@ pub async fn process_nft(
BalanceOperand::Addition,
);
pending_effects.set_tree("nfts", nft_save_name.as_bytes().to_vec(), b"1".to_vec());
pending_effects.set_tree("nft_origins", nft_save_name.as_bytes().to_vec(), txhash_bytes.clone());
pending_effects.append_tree("nft_history", nft_save_name.into_bytes(), txhash_bytes.clone());
pending_effects.set_tree(
"nft_origins",
nft_save_name.as_bytes().to_vec(),
txhash_bytes.clone(),
);
pending_effects.append_tree(
"nft_history",
nft_save_name.into_bytes(),
txhash_bytes.clone(),
);
}
// Record the txid location so RPC lookups can resolve the saved

View File

@ -373,12 +373,8 @@ fn apply_effect(db: &Db, effect: &PendingEffect) -> Result<AppliedEffect, String
contract_id,
payment,
} => {
let previous = append_tree_value(
db,
"contract_payments",
contract_id,
&payment.to_le_bytes(),
)?;
let previous =
append_tree_value(db, "contract_payments", contract_id, &payment.to_le_bytes())?;
Ok(AppliedEffect::TreeMutation {
tree: "contract_payments",
key: contract_id.clone(),
@ -400,8 +396,10 @@ fn rollback_effect(db: &Db, effect: AppliedEffect) -> Result<(), String> {
amount,
coin,
operand,
} => balance_sheet_operation_with_db(db, &address, amount, &coin, operand.inverse().as_str())
.map_err(|err| format!("Failed to roll back balance effect: {err}")),
} => {
balance_sheet_operation_with_db(db, &address, amount, &coin, operand.inverse().as_str())
.map_err(|err| format!("Failed to roll back balance effect: {err}"))
}
AppliedEffect::TreeMutation {
tree,
key,
@ -428,7 +426,12 @@ fn rollback_effect(db: &Db, effect: AppliedEffect) -> Result<(), String> {
previous_rollback,
} => {
restore_vanity_mapping(db, &owner_address, previous_vanity)?;
restore_tree_value(db, WALLET_VANITY_ROLLBACK_TREE, &rollback_key, previous_rollback)
restore_tree_value(
db,
WALLET_VANITY_ROLLBACK_TREE,
&rollback_key,
previous_rollback,
)
}
AppliedEffect::Noop => Ok(()),
}
@ -581,8 +584,8 @@ fn apply_vanity_effect(
) -> Result<AppliedEffect, String> {
let previous_vanity = get_registered_vanity_for_owner(db, owner_address)
.map_err(|err| format!("Could not read existing vanity mapping: {err}"))?;
let rollback_key =
crate::decode(txhash).map_err(|_| "Could not decode vanity transaction hash".to_string())?;
let rollback_key = crate::decode(txhash)
.map_err(|_| "Could not decode vanity transaction hash".to_string())?;
let rollback_tree = db
.open_tree(WALLET_VANITY_ROLLBACK_TREE)
.map_err(|err| format!("Could not open vanity rollback tree: {err}"))?;

View File

@ -1,6 +1,11 @@
use crate::common::check_genesis::genesis_checkup;
use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::miner::flag::{is_mining_running, is_normal_mode, is_reorganizing_mode, is_syncing_mode};
use crate::decode;
use crate::fs;
use crate::log::{error, info};
use crate::miner::flag::{
is_mining_running, is_normal_mode, is_reorganizing_mode, is_syncing_mode,
};
use crate::orphans::snapshot_check::{snapshot_height, update_snapshot};
use crate::records::block_height::get_block_height::get_height;
use crate::records::block_height::increase_block_height::increase_height;
@ -8,8 +13,8 @@ use crate::records::memory::averages::{calculate_averages, update_block_data};
use crate::records::memory::mempool::{
apply_selected_transaction_math, mark_processed_by_signatures,
mark_selected_transactions_processed, restore_processed_by_signatures,
restore_selected_transactions_processed, select_transactions_for_block, spawn_processed_cleanup,
stream_selected_transaction_originals,
restore_selected_transactions_processed, select_transactions_for_block,
spawn_processed_cleanup, stream_selected_transaction_originals,
};
use crate::records::memory::network_mapping::NodeInfo;
use crate::records::memory::torrent_status::prune_torrent_statuses_through_height;
@ -25,10 +30,7 @@ use crate::records::unpack_block::unpack_header::load_block_header;
use crate::torrent::create_metadata::{broadcast_new_torrent_to_peers, metadata_from_file};
use crate::torrent::torrenting_system::save_torrent::prune_staged_torrents;
use crate::torrent::torrenting_system::torrent_cache::prune_recent_torrents;
use crate::log::{error, info};
use crate::Arc;
use crate::decode;
use crate::fs;
use crate::Mutex;
use crate::PathBuf;
use crate::Utc;
@ -337,7 +339,9 @@ async fn save_binary_data_with_mempool_stream(
let _ = update_snapshot(db, next_number).await;
if let Some(snapshot_height) = snapshot_height(db).await {
if let Err(err) = finalize_rewards_through_height(db, snapshot_height).await {
error!("Failed to finalize rewards through snapshot height {snapshot_height}: {err}");
error!(
"Failed to finalize rewards through snapshot height {snapshot_height}: {err}"
);
}
prune_recent_torrents(snapshot_height).await;
prune_torrent_statuses_through_height(snapshot_height).await;
@ -465,7 +469,9 @@ async fn save_binary_data(params: SaveBinaryDataParams<'_>) -> Result<(), String
let _ = update_snapshot(db, next_number).await;
if let Some(snapshot_height) = snapshot_height(db).await {
if let Err(err) = finalize_rewards_through_height(db, snapshot_height).await {
error!("Failed to finalize rewards through snapshot height {snapshot_height}: {err}");
error!(
"Failed to finalize rewards through snapshot height {snapshot_height}: {err}"
);
}
prune_recent_torrents(snapshot_height).await;
prune_torrent_statuses_through_height(snapshot_height).await;

View File

@ -23,8 +23,7 @@ pub async fn process_token(
// Serialize the token-creation transaction and compute its txid
// before applying the balance-sheet and token-registry updates.
let txhash = transaction.unsigned_create_token.hash().await;
let txhash_bytes =
decode(&txhash).map_err(|e| format!("Error decoding token txhash: {e}"))?;
let txhash_bytes = decode(&txhash).map_err(|e| format!("Error decoding token txhash: {e}"))?;
let transaction_bytes = match transaction.to_bytes().await {
Ok(bytes) => bytes,
Err(e) => return Err(e.to_string()),
@ -63,7 +62,11 @@ pub async fn process_token(
let origin_key = transaction.unsigned_create_token.ticker.clone();
let origin_value = txhash.as_bytes();
pending_effects.set_tree("token_origins", origin_key.into_bytes(), origin_value.to_vec());
pending_effects.set_tree(
"token_origins",
origin_key.into_bytes(),
origin_value.to_vec(),
);
pending_effects.append_tree(
"token_history",
transaction.unsigned_create_token.ticker.as_bytes().to_vec(),

View File

@ -15,9 +15,10 @@ pub mod structs;
pub use helpers::{get_registered_pubkey, is_registered_short_address, short_address_exists};
pub use mappings::{
get_registered_vanity_for_owner, list_registered_wallets, require_canonical_registered_short_address,
resolve_canonical_registered_short_address, resolve_local_input_short_address,
resolve_owner_from_vanity_address, resolve_pubkey_from_short_address, take_previous_vanity_for_txid,
get_registered_vanity_for_owner, list_registered_wallets,
require_canonical_registered_short_address, resolve_canonical_registered_short_address,
resolve_local_input_short_address, resolve_owner_from_vanity_address,
resolve_pubkey_from_short_address, take_previous_vanity_for_txid,
};
pub use storage::{
register_or_update_vanity_address, register_short_address, remove_registered_vanity_for_owner,

View File

@ -1,15 +1,15 @@
use crate::common::network_startup::get_listen_ip;
use crate::io;
use crate::rpc::client::handshake_message::prepare_handshake_message;
use crate::rpc::client::handshake_processing::process_handshake_response;
use crate::rpc::client::structs::{Connect, Handshake};
use crate::rpc::command_maps::{MAX_RPC_REPLY_BYTES, RPC_REPLY};
use crate::rpc::handshake_constants::HANDSHAKE_RESPONSE_BYTES;
use crate::wallets::structures::Wallet;
use crate::{AsyncReadExt, AsyncWriteExt};
use crate::io;
use crate::IpAddr;
use crate::SocketAddr;
use crate::TcpStream;
use crate::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpSocket;
pub async fn connect_and_handshake(params: Connect) -> Result<(), Box<dyn std::error::Error>> {

View File

@ -1,12 +1,12 @@
use crate::common::binary_conversions::ip_port_to_binary;
use crate::common::network_startup::get_ip_and_port;
use crate::common::skein::skein_256_hash_data;
use crate::decode;
use crate::io;
use crate::rpc::commands::time::request_time;
use crate::rpc::handshake_constants::HANDSHAKE_REQUEST_BYTES;
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::decode;
use crate::io;
pub async fn prepare_handshake_message(wallet: &Wallet, message: &str) -> io::Result<Vec<u8>> {
// Client handshakes are assembled from the signed message, wallet identity, timestamp,

View File

@ -1,40 +1,46 @@
use crate::common::binary_conversions::binary_to_ip_port;
use crate::common::check_genesis::genesis_checkup;
use crate::common::network_startup::get_ip_and_port;
use crate::common::skein::skein_256_hash_data;
use crate::config::SETTINGS;
use crate::miner::flag::{clear_mining_stop_request, request_mining_stop, set_mining_state, set_node_mode, MiningState, NodeMode};
use crate::encode;
use crate::io;
use crate::log::{error, info, warn};
use crate::miner::flag::{
clear_mining_stop_request, request_mining_stop, set_mining_state, set_node_mode, MiningState,
NodeMode,
};
use crate::orphans::structs::OrphanCheckup2;
use crate::orphans::sync_check::sync_checkup;
use crate::orphans::torrent_candidates::hydrate_torrent_candidates;
use crate::records::block_height::get_block_height::get_height;
use crate::records::memory::connections::{set_reconnect_context, CONNECTIONS};
use crate::records::memory::enums::{ClientType, ConnectionType};
use crate::records::memory::response_channels::{reserve_entry, Command};
use crate::records::memory::network_mapping::NodeInfo;
use crate::records::memory::response_channels::{reserve_entry, Command};
use crate::records::memory::structs::{Connection, StoreConnectionParams};
use crate::rpc::client::handshake::connect_and_handshake;
use crate::rpc::client::register_wallet::register_connected_wallet;
use crate::rpc::client::structs::{Connect, Handshake};
use crate::rpc::client::syncing::node_syncing;
use crate::rpc::client::register_wallet::register_connected_wallet;
use crate::rpc::client::wallet_registry_sync::sync_wallet_registry;
use crate::rpc::command_maps::RPC_RANDOM_NODE;
use crate::rpc::handshake_constants::{HANDSHAKE_ADDRESS_OFFSET, HANDSHAKE_MESSAGE_BYTES, HANDSHAKE_RESPONSE_BYTES, HANDSHAKE_SIGNATURE_OFFSET,};
use crate::rpc::handshake_constants::{
HANDSHAKE_ADDRESS_OFFSET, HANDSHAKE_MESSAGE_BYTES, HANDSHAKE_RESPONSE_BYTES,
HANDSHAKE_SIGNATURE_OFFSET,
};
use crate::rpc::responses::RpcResponse;
use crate::rpc::server::rpc_command_loop::start_loop;
use crate::common::network_startup::get_ip_and_port;
use crate::sled::Db;
use crate::startup::network_broadcast::announce_self_to_network;
use crate::startup::remote_height::request_remote_height;
use crate::timeout;
use crate::wallets::structures::Wallet;
use crate::log::{error, info, warn};
use crate::sled::Db;
use crate::Arc;
use crate::Duration;
use crate::encode;
use crate::io;
use crate::Mutex;
use crate::SocketAddr;
use crate::TcpStream;
use crate::timeout;
#[derive(Clone)]
pub struct BootstrapParams {
@ -104,7 +110,10 @@ pub async fn bootstrap_peer_discovery(mut params: BootstrapParams) -> Result<(),
continue;
}
if Connection::get_stream_from_memory(&addr_string).await.is_some() {
if Connection::get_stream_from_memory(&addr_string)
.await
.is_some()
{
no_progress_count += 1;
continue;
}

View File

@ -1,15 +1,15 @@
use crate::common::skein::skein_256_hash_bytes;
use crate::decode;
use crate::log::warn;
use crate::records::memory::response_channels::{reserve_entry, Command};
use crate::rpc::command_maps::RPC_REGISTER_WALLET;
use crate::rpc::responses::RpcResponse;
use crate::timeout;
use crate::wallets::structures::Wallet;
use crate::log::warn;
use crate::Arc;
use crate::decode;
use crate::Duration;
use crate::Mutex;
use crate::TcpStream;
use crate::timeout;
pub async fn register_connected_wallet(
stream: Arc<Mutex<TcpStream>>,

View File

@ -1,19 +1,21 @@
use crate::common::check_genesis::genesis_checkup;
use crate::io;
use crate::log::{error, info, warn};
use crate::orphans::structs::OrphanCheckup2;
use crate::orphans::sync_check::sync_checkup;
use crate::records::block_height::get_block_height::get_height;
use crate::records::memory::response_channels::reserve_entry;
use crate::records::memory::response_channels::Command;
use crate::torrent::structs::Torrent;
use crate::torrent::torrenting_system::torrent_requests::{handle_response_and_save_torrent, send_request_torrent_message};
use crate::log::{error, info, warn};
use crate::sled::Db;
use crate::timeout;
use crate::torrent::structs::Torrent;
use crate::torrent::torrenting_system::torrent_requests::{
handle_response_and_save_torrent, send_request_torrent_message,
};
use crate::Arc;
use crate::Duration;
use crate::io;
use crate::Mutex;
use crate::TcpStream;
use crate::timeout;
pub async fn node_syncing(
stream: Arc<Mutex<TcpStream>>,

View File

@ -1,16 +1,16 @@
use crate::log::warn;
use crate::records::memory::response_channels::{reserve_entry, Command};
use crate::records::wallet_registry::{register_short_address, WalletRegistrationResult};
use crate::rpc::commands::wallet_registry_sync::WALLET_REGISTRY_RECORD_BYTES;
use crate::rpc::command_maps::RPC_WALLET_REGISTRY_SYNC;
use crate::rpc::commands::wallet_registry_sync::WALLET_REGISTRY_RECORD_BYTES;
use crate::rpc::responses::RpcResponse;
use crate::log::warn;
use crate::sled::Db;
use crate::timeout;
use crate::wallets::structures::Wallet;
use crate::Arc;
use crate::Duration;
use crate::Mutex;
use crate::TcpStream;
use crate::timeout;
pub async fn sync_wallet_registry(
stream: Arc<Mutex<TcpStream>>,

View File

@ -1,10 +1,10 @@
use crate::records::memory::response_channels::Command;
use crate::records::memory::network_mapping::NodeInfo;
use crate::records::memory::network_mapping::structs::{AddAddressParams, SignedNodeEdit};
use crate::records::memory::network_mapping::NodeInfo;
use crate::records::memory::response_channels::Command;
use crate::rpc::read_bytes_from_stream;
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
use crate::Arc;
use crate::Mutex;
use crate::TcpStream;
@ -21,9 +21,11 @@ pub async fn add_network_node(
let (uid, _) =
read_bytes_from_stream::read_uid_from_stream(connections_key, stream_locked.clone())
.await?;
let address_bytes =
read_bytes_from_stream::read_short_address_from_stream(connections_key, stream_locked.clone())
.await?;
let address_bytes = read_bytes_from_stream::read_short_address_from_stream(
connections_key,
stream_locked.clone(),
)
.await?;
let address = Wallet::bytes_to_short_address(&address_bytes)
.ok_or_else(|| "error: Invalid short address bytes".to_string())?;
let ip =

View File

@ -1,8 +1,8 @@
use crate::records::balance_sheet::get_wallet_balance::get_balance;
use crate::records::wallet_registry::resolve_canonical_registered_short_address;
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
pub async fn lookup_wallet_coin(db: &Db, address: String, coin: String) -> RpcResponse {
// Return the saved confirmed balance for a specific address/asset pair.

View File

@ -1,14 +1,14 @@
use crate::common::nft_assets::nft_asset_parts;
use crate::log::error;
use crate::read_dir;
use crate::records::balance_sheet::pathing::{address_root_path, asset_name_from_relative_path};
use crate::records::wallet_registry::resolve_canonical_registered_short_address;
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::log::error;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
use crate::AsyncReadExt;
use crate::File;
use crate::Path;
use crate::read_dir;
pub async fn get_token_balances(db: &Db, address: String) -> RpcResponse {
// Walk the hierarchical balance-sheet tree for one address and emit

View File

@ -9,9 +9,7 @@ pub async fn request_block(db: &Db, hash: &str) -> RpcResponse {
let hash_bytes = match decode(hash) {
Ok(bytes) => bytes,
Err(err) => {
return RpcResponse::Binary(
format!("error: Failed to decode hash: {err}").into_bytes(),
)
return RpcResponse::Binary(format!("error: Failed to decode hash: {err}").into_bytes())
}
};
@ -57,8 +55,6 @@ pub async fn request_block(db: &Db, hash: &str) -> RpcResponse {
format!("error: Failed to convert block to bytes: {err}").into_bytes(),
),
},
Err(err) => {
RpcResponse::Binary(format!("error: Failed to load block: {err}").into_bytes())
}
Err(err) => RpcResponse::Binary(format!("error: Failed to load block: {err}").into_bytes()),
}
}

View File

@ -1,6 +1,6 @@
use crate::decode;
use crate::rpc::responses::RpcResponse;
use crate::sled::Db;
use crate::decode;
pub async fn lookup_by_hash(db: &Db, hash: &str) -> RpcResponse {
// Resolve the block hash through the block-hash index and then fetch

View File

@ -1,6 +1,6 @@
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
pub async fn block_peer(db: &Db, ip: String, signature: String, wallet_key: String) -> RpcResponse {
// Peer blocking is restricted to the local node owner, proven by a

View File

@ -1,12 +1,12 @@
use crate::blocks::collateral::CollateralClaimTransaction;
use crate::blocks::loan_payment::ContractPaymentTransaction;
use crate::blocks::loans::LoanContractTransaction;
use crate::encode;
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
use crate::{DateTime, Datelike, Local, TimeZone, Utc};
use crate::encode;
fn format_amount(value: u64) -> f64 {
// Contract RPC output presents coin amounts as user-facing decimal values.
@ -103,8 +103,7 @@ async fn collect_contract_activity(
let mut collateral_claim: Option<CollateralClaimTransaction> = None;
for entry in tree.iter() {
let (txid_bytes, _) =
entry.map_err(|e| format!("error: Failed to read txid tree: {e}"))?;
let (txid_bytes, _) = entry.map_err(|e| format!("error: Failed to read txid tree: {e}"))?;
let RpcResponse::Binary(bytes) = request_transaction_by_txid(db, txid_bytes.to_vec()).await;
if bytes.is_empty() {
continue;
@ -243,8 +242,7 @@ pub async fn contract_details(hash: Vec<u8>, db: &Db) -> RpcResponse {
pub async fn contract_details_by_address(address: String, db: &Db) -> RpcResponse {
// Return every saved contract where the address appears as either the
// lender or borrower, each expanded into the same text summary view.
let Some(address) = Wallet::normalize_to_short_address(&address)
else {
let Some(address) = Wallet::normalize_to_short_address(&address) else {
return RpcResponse::Binary(b"error: Invalid wallet address".to_vec());
};

View File

@ -1,10 +1,10 @@
use crate::records::memory::response_channels::Command;
use crate::records::memory::network_mapping::NodeInfo;
use crate::records::memory::network_mapping::structs::{DeleteAddressParams, SignedNodeEdit};
use crate::records::memory::network_mapping::NodeInfo;
use crate::records::memory::response_channels::Command;
use crate::rpc::read_bytes_from_stream;
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
use crate::Arc;
use crate::Mutex;
use crate::TcpStream;
@ -21,9 +21,11 @@ pub async fn delete_network_node(
let (uid, _) =
read_bytes_from_stream::read_uid_from_stream(connections_key, stream_locked.clone())
.await?;
let address_bytes =
read_bytes_from_stream::read_short_address_from_stream(connections_key, stream_locked.clone())
.await?;
let address_bytes = read_bytes_from_stream::read_short_address_from_stream(
connections_key,
stream_locked.clone(),
)
.await?;
let address = Wallet::bytes_to_short_address(&address_bytes)
.ok_or_else(|| "error: Invalid short address bytes".to_string())?;
let ip =

View File

@ -1,7 +1,7 @@
// The rpc commands module groups the request handlers that run after a client handshake succeeds.
pub mod add_network_node;
pub mod address_coin_lookup;
pub mod address_complete_balance_sheet;
pub mod add_network_node;
pub mod bad_rpc_call;
pub mod block_by_hash;
pub mod block_by_height;
@ -11,10 +11,10 @@ pub mod block_headers;
pub mod block_height;
pub mod block_peer_ip;
pub mod contract;
pub mod difficulty;
pub mod delete_network_node;
pub mod latest_block;
pub mod difficulty;
pub mod largest_tx_fee;
pub mod latest_block;
pub mod memory_by_signature;
pub mod network_info;
pub mod nft_list;
@ -31,14 +31,14 @@ pub mod torrent;
pub mod torrent_by_block;
pub mod torrent_candidates;
pub mod transaction_by_txid;
pub mod transactions_by_address;
pub mod tx_count;
pub mod tx_count_from_mempool;
pub mod tx_submit;
pub mod transactions_by_address;
pub mod unblock_peer_ip;
pub mod validate_address;
pub mod validate_torrent;
pub mod validate_message;
pub mod validate_torrent;
pub mod wallet_register;
pub mod wallet_registry_sync;
pub mod wallet_vanity_lookup;

View File

@ -1,7 +1,7 @@
use crate::common::nft_assets::nft_asset_parts;
use crate::encode;
use crate::rpc::responses::RpcResponse;
use crate::sled::Db;
use crate::encode;
pub async fn get_nfts(db: &Db) -> RpcResponse {
// Serialize every NFT asset as origin hash, padded asset name, and

View File

@ -6,14 +6,16 @@ use crate::blocks::swap::SwapTransaction;
use crate::blocks::transfer::TransferTransaction;
use crate::common::binary_conversions::binary_to_string;
use crate::common::nft_assets::{nft_asset_name, nft_asset_parts};
use crate::records::balance_sheet::pathing::{balance_asset_segments, balance_root_path};
use crate::records::balance_sheet::tokens_to_lower::strip_spaces_and_lowercase;
use crate::rpc::commands::transaction_by_txid::{request_transaction_by_txid, request_transaction_by_txid_with_block};
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::sled::Db;
use crate::decode;
use crate::fs;
use crate::records::balance_sheet::pathing::{balance_asset_segments, balance_root_path};
use crate::records::balance_sheet::tokens_to_lower::strip_spaces_and_lowercase;
use crate::rpc::commands::transaction_by_txid::{
request_transaction_by_txid, request_transaction_by_txid_with_block,
};
use crate::rpc::responses::RpcResponse;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
const ACTION_CREATE: u8 = 1;
const ACTION_TRANSFER: u8 = 2;
@ -102,7 +104,8 @@ async fn find_nft_origin(
let txid_tree = db.open_tree("txid").ok()?;
for entry in txid_tree.iter() {
let (txid_bytes, _) = entry.ok()?;
let RpcResponse::Binary(tx_bytes) = request_transaction_by_txid(db, txid_bytes.to_vec()).await;
let RpcResponse::Binary(tx_bytes) =
request_transaction_by_txid(db, txid_bytes.to_vec()).await;
if tx_bytes.is_empty() || tx_bytes[0] != 4 {
continue;
@ -173,7 +176,8 @@ async fn find_current_holder(asset_name: &str) -> String {
async fn build_history_entry(db: &Db, asset_name: &str, txid_bytes: &[u8]) -> Option<Vec<u8>> {
// Expand a raw NFT history txid into a fixed-width history entry that
// captures the action, involved wallets, and any received asset details.
let RpcResponse::Binary(response) = request_transaction_by_txid_with_block(db, txid_bytes.to_vec()).await;
let RpcResponse::Binary(response) =
request_transaction_by_txid_with_block(db, txid_bytes.to_vec()).await;
if response.len() < 5 {
return None;
@ -267,7 +271,8 @@ async fn build_history_entry(db: &Db, asset_name: &str, txid_bytes: &[u8]) -> Op
.await
.ok()?;
let contract_hash = decode(&tx.unsigned_contract_payment.contract_hash).ok()?;
let RpcResponse::Binary(contract_bytes) = request_transaction_by_txid(db, contract_hash).await;
let RpcResponse::Binary(contract_bytes) =
request_transaction_by_txid(db, contract_hash).await;
let contract = LoanContractTransaction::from_bytes(7, &contract_bytes)
.await
.ok()?;
@ -285,7 +290,8 @@ async fn build_history_entry(db: &Db, asset_name: &str, txid_bytes: &[u8]) -> Op
.await
.ok()?;
let contract_hash = decode(&tx.unsigned_collateral_claim.contract_hash).ok()?;
let RpcResponse::Binary(contract_bytes) = request_transaction_by_txid(db, contract_hash).await;
let RpcResponse::Binary(contract_bytes) =
request_transaction_by_txid(db, contract_hash).await;
let contract = LoanContractTransaction::from_bytes(7, &contract_bytes)
.await
.ok()?;
@ -339,7 +345,8 @@ pub async fn lookup_nft_details(db: &Db, nft_name: String, item_number: u32) ->
return RpcResponse::Binary(b"error: NFT genesis not found".to_vec());
};
let RpcResponse::Binary(genesis_response) = request_transaction_by_txid(db, genesis_bytes.clone()).await;
let RpcResponse::Binary(genesis_response) =
request_transaction_by_txid(db, genesis_bytes.clone()).await;
if genesis_response.is_empty() || genesis_response[0] != 4 {
return RpcResponse::Binary(b"error: NFT genesis transaction not found".to_vec());
}

View File

@ -1,5 +1,7 @@
use crate::common::check_genesis::genesis_checkup;
use crate::common::skein::skein_128_hash_bytes;
use crate::lazy_static;
use crate::log::{error, warn};
use crate::miner::flag::{is_reorganizing_mode, is_syncing_mode};
use crate::orphans::structs::OrphanCheckup2;
use crate::orphans::sync_check::sync_checkup;
@ -8,17 +10,19 @@ use crate::records::memory::response_channels::Command;
use crate::rpc::read_bytes_from_stream;
use crate::rpc::responses::RpcResponse;
use crate::rpc::server::flood_protection::MAX_TORRENT_METADATA_BYTES;
use crate::startup::remote_height::request_remote_height;
use crate::torrent::structs::Torrent;
use crate::torrent::create_metadata::broadcast_new_torrent_to_peers;
use crate::torrent::torrenting_system::torrent_requests::{setup_download_for_torrent, stage_and_verify_torrent};
use crate::torrent::torrenting_system::torrent_cache::{has_recent_torrent, remember_recent_torrent};
use crate::log::{error, warn};
use crate::sled::Db;
use crate::startup::remote_height::request_remote_height;
use crate::torrent::create_metadata::broadcast_new_torrent_to_peers;
use crate::torrent::structs::Torrent;
use crate::torrent::torrenting_system::torrent_cache::{
has_recent_torrent, remember_recent_torrent,
};
use crate::torrent::torrenting_system::torrent_requests::{
setup_download_for_torrent, stage_and_verify_torrent,
};
use crate::Arc;
use crate::AtomicBool;
use crate::AtomicOrdering;
use crate::lazy_static;
use crate::Mutex;
lazy_static! {
@ -156,8 +160,7 @@ pub async fn torrent_submission(
return TorrentSubmissionOutcome::Rejected(RpcResponse::Binary(msg));
}
match stage_and_verify_torrent(height, db, torrent, wallet_key, process_now).await
{
match stage_and_verify_torrent(height, db, torrent, wallet_key, process_now).await {
Ok(stage_result) => {
let _ = remember_recent_torrent(&torrent_hash, height).await;
if let Some((torrent, staged_path)) = stage_result {
@ -273,7 +276,8 @@ pub async fn receive_torrent(
) -> Result<(u32, RpcResponse), String> {
let (uid, _) =
read_bytes_from_stream::read_uid_from_stream(connections_key, stream.clone()).await?;
let size = read_bytes_from_stream::read_u32_from_stream(connections_key, stream.clone()).await?;
let size =
read_bytes_from_stream::read_u32_from_stream(connections_key, stream.clone()).await?;
// The size includes the block-height field, so the remaining bytes
// are the torrent metadata that will be parsed and staged.

View File

@ -1,9 +1,11 @@
use crate::records::memory::enums::ClientType;
use crate::records::memory::response_channels::{delete_entry, get_entry, is_retired_entry, Command};
use crate::rpc::commands::bad_rpc_call;
use crate::rpc::command_maps::MAX_RPC_REPLY_BYTES;
use crate::rpc::read_bytes_from_stream;
use crate::log::warn;
use crate::records::memory::enums::ClientType;
use crate::records::memory::response_channels::{
delete_entry, get_entry, is_retired_entry, Command,
};
use crate::rpc::command_maps::MAX_RPC_REPLY_BYTES;
use crate::rpc::commands::bad_rpc_call;
use crate::rpc::read_bytes_from_stream;
use crate::sled::Db;
use crate::Arc;
use crate::Mutex;
@ -24,8 +26,8 @@ pub async fn route_reply(
read_bytes_from_stream::read_uid_from_stream(connections_key, stream_locked.clone())
.await?;
let message_length =
read_bytes_from_stream::read_u32_from_stream(connections_key, stream_locked.clone())
.await? as usize;
read_bytes_from_stream::read_u32_from_stream(connections_key, stream_locked.clone()).await?
as usize;
if message_length > MAX_RPC_REPLY_BYTES {
bad_rpc_call::record(ip, client_type, db, wallet_key).await;
return Err(format!(
@ -44,9 +46,7 @@ pub async fn route_reply(
)
.await?;
if tx.send(buffer).await.is_err() {
warn!(
"[rpc] reply receiver dropped before payload delivery: {uid:?}"
);
warn!("[rpc] reply receiver dropped before payload delivery: {uid:?}");
}
delete_entry(map, uid).await;

View File

@ -1,14 +1,14 @@
use crate::blocks::token::CreateTokenTransaction;
use crate::common::binary_conversions::binary_to_string;
use crate::fs;
use crate::records::balance_sheet::pathing::{balance_asset_segments, balance_root_path};
use crate::records::balance_sheet::tokens_to_lower::strip_spaces_and_lowercase;
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::sled::{Db, Tree};
use crate::{decode, encode};
use crate::fs;
use crate::wallets::structures::Wallet;
use crate::PathBuf;
use crate::{decode, encode};
fn parse_token_supply(value: &[u8]) -> Option<u64> {
// Token supply may be stored either as raw bytes or as a decimal string.
@ -85,7 +85,8 @@ async fn find_origin_hash(
Err(_) => continue,
};
let RpcResponse::Binary(tx_bytes) = request_transaction_by_txid(db, txid_bytes.to_vec()).await;
let RpcResponse::Binary(tx_bytes) =
request_transaction_by_txid(db, txid_bytes.to_vec()).await;
// The fallback only cares about create-token transactions because
// those define the origin hash for a token.
@ -221,7 +222,8 @@ pub async fn lookup_token_details(db: &Db, token_name: String) -> RpcResponse {
None => return RpcResponse::Binary(b"error: Token origin not found".to_vec()),
};
let RpcResponse::Binary(tx_bytes) = request_transaction_by_txid(db, decode(&origin_hash).unwrap_or_default()).await;
let RpcResponse::Binary(tx_bytes) =
request_transaction_by_txid(db, decode(&origin_hash).unwrap_or_default()).await;
if tx_bytes.is_empty() {
return RpcResponse::Binary(b"error: Token contract transaction not found".to_vec());

View File

@ -1,30 +1,12 @@
use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::common::skein::skein_128_hash_bytes;
use crate::rpc::responses::RpcResponse;
use crate::torrent::structs::Torrent;
use crate::sled::Db;
use crate::{AsyncReadExt, AsyncSeekExt, SeekFrom};
use crate::{decode, encode};
use crate::torrent::structs::Torrent;
use crate::File;
use crate::Path;
use crate::PathBuf;
fn remove_block_pieces_from_db(db: &Db, block_number: u32, info_hash: &str) {
// When the canonical torrent exists, temporary cached pieces for that
// block are no longer needed and can be dropped from the piece cache.
let Ok(tree) = db.open_tree("block_pieces") else {
return;
};
let prefix = format!("{block_number}-{info_hash}-");
let iter = tree.range(prefix.as_bytes()..);
for (key, _value) in iter.flatten() {
if !key.starts_with(prefix.as_bytes()) {
break;
}
let _ = tree.remove(key);
}
}
use crate::{decode, encode};
use crate::{AsyncReadExt, AsyncSeekExt, SeekFrom};
pub async fn request_block_piece(
db: &Db,
@ -44,9 +26,7 @@ pub async fn request_block_piece(
}
};
let requested_info_hash_hex = encode(requested_info_hash.to_le_bytes());
let key = format!(
"{block_number}-{requested_info_hash_hex}-{requested_piece}"
);
let key = format!("{block_number}-{requested_info_hash_hex}-{requested_piece}");
let (
_network_name,
@ -68,19 +48,12 @@ pub async fn request_block_piece(
.to_string_lossy()
.into_owned();
let file_exists = Path::new(&torrent_filename).exists();
let prefix = format!("{block_number}-{requested_info_hash_hex}-");
let pieces_exist = tree.range(prefix.as_bytes()..).peekable().peek().is_some();
// Once the canonical torrent exists, cached block pieces for the same
// height can be purged so the canonical file becomes the source of truth.
if file_exists && pieces_exist {
remove_block_pieces_from_db(db, block_number, &requested_info_hash_hex);
}
if let Some(piece_data) = tree.get(&key).ok().and_then(|result| result) {
// Cached pieces are used for in-progress downloads before the
// canonical torrent file is available locally.
// canonical torrent file is available locally. During an orphan
// fight this must be checked before the canonical file because a
// node can have cached pieces for a competing candidate at the
// same height.
RpcResponse::Binary(piece_data.to_vec())
} else if let Ok(mut torrent_file) = File::open(&torrent_filename).await {
let mut torrent_contents = Vec::new();

View File

@ -1,7 +1,7 @@
use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::read;
use crate::rpc::responses::RpcResponse;
use crate::Path;
use crate::read;
pub async fn request_block_torrent(height: &u32) -> RpcResponse {
// Torrent files live alongside blocks under a predictable

View File

@ -1,13 +1,13 @@
use crate::blocks::block::VRF_BLOCK_BYTES;
use crate::common::binary_conversions::binary_to_string;
use crate::common::network_paths_and_settings::block_extension_and_paths;
use crate::io;
use crate::rpc::command_maps;
use crate::rpc::responses::RpcResponse;
use crate::sled::Db;
use crate::{AsyncReadExt, AsyncSeekExt, SeekFrom};
use crate::File;
use crate::io;
use crate::PathBuf;
use crate::{AsyncReadExt, AsyncSeekExt, SeekFrom};
const HEADER_SIZE: u64 = VRF_BLOCK_BYTES as u64;

View File

@ -18,8 +18,8 @@ use crate::records::memory::enums::ClientType;
use crate::records::memory::response_channels::generate_uid;
use crate::rpc::command_maps::RPC_SUBMIT_TRANSACTION;
use crate::rpc::responses::RpcResponse;
use crate::torrent::torrenting_system::get_nodes::get_nodes_from_memory;
use crate::sled::Db;
use crate::torrent::torrenting_system::get_nodes::get_nodes_from_memory;
async fn broadcast_tx(tx_bytes: Vec<u8>) {
// Broadcast newly accepted mempool transactions only to miner peers,

View File

@ -1,6 +1,6 @@
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::sled::Db;
use crate::wallets::structures::Wallet;
pub async fn unblock_peer(
db: &Db,

View File

@ -1,8 +1,8 @@
use crate::rpc::read_bytes_from_stream;
use crate::rpc::responses::RpcResponse;
use crate::rpc::server::flood_protection::MAX_TORRENT_METADATA_BYTES;
use crate::torrent::structs::Torrent;
use crate::sled::Db;
use crate::torrent::structs::Torrent;
use crate::Arc;
use crate::Mutex;
use crate::TcpStream;

View File

@ -1,17 +1,17 @@
use crate::common::skein::skein_256_hash_bytes;
use crate::decode;
use crate::log::warn;
use crate::records::memory::connections::CONNECTIONS;
use crate::records::memory::response_channels::{reserve_entry, Command};
use crate::records::wallet_registry::{register_short_address, WalletRegistrationResult};
use crate::rpc::command_maps::RPC_REGISTER_WALLET;
use crate::rpc::responses::RpcResponse;
use crate::wallets::structures::Wallet;
use crate::log::warn;
use crate::sled::Db;
use crate::decode;
use crate::timeout;
use crate::wallets::structures::Wallet;
use crate::Arc;
use crate::Duration;
use crate::Mutex;
use crate::timeout;
async fn broadcast_wallet_registration(
short_address: &[u8],

Some files were not shown because too many files have changed in this diff Show More