Fix staged torrent candidate race
This commit is contained in:
parent
f92823ac90
commit
61a64cf538
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,8 +45,7 @@ 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)?)
|
||||
let wallet_prefix = String::from_utf8_lossy(response.get(offset..offset + wallet_prefix_len)?)
|
||||
.trim()
|
||||
.to_string();
|
||||
offset += wallet_prefix_len;
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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?;
|
||||
|
|
|
|||
|
|
@ -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?;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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?;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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?;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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?;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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?;
|
||||
|
|
|
|||
|
|
@ -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?;
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
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, ¶ms.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(¶ms.db, height, &torrent.info.info_hash).await?;
|
||||
|
||||
if result.len() != torrent.info.length as usize {
|
||||
cleanup_candidate_pieces(¶ms.db, height, torrent).await;
|
||||
return Err("Downloaded candidate length does not match torrent metadata.".to_string());
|
||||
}
|
||||
None => preferred = Some(torrent.clone()),
|
||||
|
||||
if skein_128_hash_bytes(&result) != torrent.info.info_hash {
|
||||
cleanup_candidate_pieces(¶ms.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(¶ms.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(¶ms.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,34 +162,17 @@ pub async fn checkup(params: OrphanCheckup, wallet_key: &str) -> Result<(), Stri
|
|||
let local_torrent = load_torrent(¶ms.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 ordered_candidates =
|
||||
ordered_competing_candidates(height, &local_torrent, &staged_candidates).await;
|
||||
|
||||
for competing_torrent in ordered_candidates {
|
||||
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(),
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
info!("[orphan] adopting competing staged chain from height {height}");
|
||||
undo_transactions(undo_transactions_params, wallet_key).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 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 !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,
|
||||
|
|
@ -126,6 +182,51 @@ pub async fn checkup(params: OrphanCheckup, wallet_key: &str) -> Result<(), Stri
|
|||
.await;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
match candidate_attaches_before_rollback(
|
||||
¶ms,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(¶ms, 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(¶ms.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(¶ms.db) < height_before_window_check
|
||||
{
|
||||
replay_waiting = true;
|
||||
}
|
||||
error!("[orphan] orphan window check error: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
let height_before_replay = get_height(¶ms.db);
|
||||
match replay_staged_torrents(¶ms, 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(¶ms.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();
|
||||
|
|
|
|||
|
|
@ -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, ¶ms.db, current_height).await
|
||||
undo_rewards_transaction(
|
||||
rewards_tx,
|
||||
&mining_receiver,
|
||||
¶ms.db,
|
||||
current_height,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Transaction::Transfer(transfer_tx) => {
|
||||
undo_transfer_transaction(transfer_tx.clone(), &mining_receiver, ¶ms.db).await;
|
||||
undo_transfer_transaction(transfer_tx.clone(), &mining_receiver, ¶ms.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, ¶ms.db)
|
||||
undo_create_token_transaction(
|
||||
create_token_tx.clone(),
|
||||
&mining_receiver,
|
||||
¶ms.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, ¶ms.db).await;
|
||||
undo_issue_token_transaction(
|
||||
issue_token_tx.clone(),
|
||||
&mining_receiver,
|
||||
¶ms.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, ¶ms.db).await;
|
||||
undo_create_nft_transaction(
|
||||
create_nft_tx.clone(),
|
||||
&mining_receiver,
|
||||
¶ms.db,
|
||||
)
|
||||
.await;
|
||||
rolled_back_transactions.push(Transaction::Nft(create_nft_tx));
|
||||
}
|
||||
Transaction::Marketing(marketing_tx) => {
|
||||
undo_marketing_transaction(marketing_tx.clone(), &mining_receiver, ¶ms.db).await;
|
||||
undo_marketing_transaction(marketing_tx.clone(), &mining_receiver, ¶ms.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, ¶ms.db).await;
|
||||
undo_loan_creation_transaction(loan_tx.clone(), &mining_receiver, ¶ms.db)
|
||||
.await;
|
||||
rolled_back_transactions.push(Transaction::Lender(loan_tx));
|
||||
}
|
||||
Transaction::Borrower(borrower_tx) => {
|
||||
undo_borrower_transaction(borrower_tx.clone(), &mining_receiver, ¶ms.db).await?;
|
||||
undo_borrower_transaction(borrower_tx.clone(), &mining_receiver, ¶ms.db)
|
||||
.await?;
|
||||
rolled_back_transactions.push(Transaction::Borrower(borrower_tx));
|
||||
}
|
||||
Transaction::Collateral(collateral_tx) => {
|
||||
undo_collateral_transaction(collateral_tx.clone(), &mining_receiver, ¶ms.db).await?;
|
||||
undo_collateral_transaction(
|
||||
collateral_tx.clone(),
|
||||
&mining_receiver,
|
||||
¶ms.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, ¶ms.db).await?;
|
||||
undo_vanity_transaction(vanity_tx.clone(), &mining_receiver, ¶ms.db)
|
||||
.await?;
|
||||
rolled_back_transactions.push(Transaction::Vanity(vanity_tx));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
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)
|
||||
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)
|
||||
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 signatures = vec![
|
||||
transaction.signature1.clone(),
|
||||
transaction.signature2.clone(),
|
||||
];
|
||||
|
||||
restore_if_spendable(&signatures, lender_spendable && borrower_spendable, || async {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -89,5 +89,4 @@ pub async fn undo_burn_transaction(transaction: BurnTransaction, mining_receiver
|
|||
&restored_supply.to_le_bytes(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,5 +45,4 @@ pub async fn undo_marketing_transaction(
|
|||
let tree = db.open_tree("txid").unwrap();
|
||||
let key = hash;
|
||||
tree.remove(key).unwrap();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,5 +67,4 @@ pub async fn undo_transfer_transaction(
|
|||
{
|
||||
let _ = remove_nft_history_entry(db, &transfer_asset, &hash);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,7 +424,9 @@ impl Connection {
|
|||
let conn = lock.as_ref()?;
|
||||
|
||||
let ip_bytes = ip_to_binary(&ip);
|
||||
conn.connection_map.iter().find_map(|(connection_key, info)| {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -395,4 +395,3 @@ pub async fn clear_mempool() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -259,5 +259,4 @@ impl NodeInfo {
|
|||
|
||||
RpcResponse::Binary(b"Success".to_vec())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -297,5 +297,4 @@ impl NodeInfo {
|
|||
|
||||
RpcResponse::Binary(b"Success: Node marked as deleted".to_vec())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,5 +73,4 @@ impl NodeInfo {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}"))?;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>> {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>>,
|
||||
|
|
|
|||
|
|
@ -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>>,
|
||||
|
|
|
|||
|
|
@ -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>>,
|
||||
|
|
|
|||
|
|
@ -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,8 +21,10 @@ 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())
|
||||
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())?;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,8 +21,10 @@ 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())
|
||||
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())?;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue