fixed bug in prevalidation mempool lookup

This commit is contained in:
viraladmin 2026-07-21 10:47:28 -06:00
parent ac7c0f0558
commit 8d0007eb1a
30 changed files with 230 additions and 199 deletions

View File

@ -19,7 +19,7 @@ GPT-5.6 SOL assisted with:
- Hardening network-map monitoring and connection-state handling. - Hardening network-map monitoring and connection-state handling.
- Implementing and reviewing on-chain data-storage transaction support. - Implementing and reviewing on-chain data-storage transaction support.
- Integrating data storage, address data, NFTs, loans, assets, and transaction history into the GUI wallet. - Integrating data storage, address data, NFTs, loans, assets, and transaction history into the GUI wallet.
- Improving GUI wallet cache synchronization, cache deletion safety, and Windows file-access coordination. - Improving [GUI wallet](https://contractless.dev/contractless/Contractless-GUI-Wallet) cache synchronization, cache deletion safety, and Windows file-access coordination.
- Reviewing Rust transaction validation, mempool handling, block saving, and chain-reorganization paths. - Reviewing Rust transaction validation, mempool handling, block saving, and chain-reorganization paths.
- Identifying behavioral inconsistencies between local wallet caches and RPC responses. - Identifying behavioral inconsistencies between local wallet caches and RPC responses.
- Expanding and reorganizing user documentation for installation, wallets, transactions, validation tools, loans, swaps, NFTs, fees, and data storage. - Expanding and reorganizing user documentation for installation, wallets, transactions, validation tools, loans, swaps, NFTs, fees, and data storage.
@ -90,5 +90,3 @@ Use the release package that matches your operating system and follow the matchi
## Current Status ## Current Status
Contractless is still under active testnet development. Interfaces, transaction tools, docs, and node behavior may continue to change while the chain is tested. Contractless is still under active testnet development. Interfaces, transaction tools, docs, and node behavior may continue to change while the chain is tested.
If you are interested in running a testnet node, start with the installation guide for your operating system, then review the configuration and wallet tools documentation.

View File

@ -50,6 +50,10 @@ pub struct VrfBlock {
pub struct Block { pub struct Block {
pub vrf_block: VrfBlock, pub vrf_block: VrfBlock,
pub transactions: Vec<Transaction>, pub transactions: Vec<Transaction>,
// Exact transaction slices are retained for downloaded/stored blocks so
// mempool reuse can compare bytes without serializing or hashing again.
#[serde(skip)]
pub original_transactions: Vec<Vec<u8>>,
} }
impl UnminedBlock { impl UnminedBlock {

View File

@ -131,3 +131,39 @@ pub enum Transaction {
StorageI128(StorageI128), StorageI128(StorageI128),
DeleteKey(DeleteKey), DeleteKey(DeleteKey),
} }
impl Transaction {
// Return the signature used by the indexed mempool lookup. Two-party
// transactions use the final signer because valid submitted swaps and
// loan contracts must already contain both signatures.
pub fn mempool_signature(&self) -> Option<&str> {
match self {
Transaction::Genesis(_) | Transaction::Rewards(_) => None,
Transaction::Transfer(tx) => Some(&tx.signature),
Transaction::Burn(tx) => Some(&tx.signature),
Transaction::Token(tx) => Some(&tx.signature),
Transaction::IssueToken(tx) => Some(&tx.signature),
Transaction::Nft(tx) => Some(&tx.signature),
Transaction::Marketing(tx) => Some(&tx.signature),
Transaction::Swap(tx) => Some(&tx.signature2),
Transaction::Lender(tx) => Some(&tx.signature2),
Transaction::Borrower(tx) => Some(&tx.signature),
Transaction::Collateral(tx) => Some(&tx.signature),
Transaction::Vanity(tx) => Some(&tx.signature),
Transaction::StorageKey(tx) => Some(&tx.signature),
Transaction::StorageBool(tx) => Some(&tx.signature),
Transaction::StorageU8(tx) => Some(&tx.signature),
Transaction::StorageU16(tx) => Some(&tx.signature),
Transaction::StorageU32(tx) => Some(&tx.signature),
Transaction::StorageU64(tx) => Some(&tx.signature),
Transaction::StorageU128(tx) => Some(&tx.signature),
Transaction::StorageString(tx) => Some(&tx.signature),
Transaction::StorageI8(tx) => Some(&tx.signature),
Transaction::StorageI16(tx) => Some(&tx.signature),
Transaction::StorageI32(tx) => Some(&tx.signature),
Transaction::StorageI64(tx) => Some(&tx.signature),
Transaction::StorageI128(tx) => Some(&tx.signature),
Transaction::DeleteKey(tx) => Some(&tx.signature),
}
}
}

View File

@ -133,6 +133,7 @@ async fn create_genesis_block(
let block = Block { let block = Block {
vrf_block, vrf_block,
transactions: vec![Transaction::Genesis(signed_genesis_transaction.clone())], transactions: vec![Transaction::Genesis(signed_genesis_transaction.clone())],
original_transactions: Vec::new(),
}; };
// Reuse normal block verification before saving so genesis // Reuse normal block verification before saving so genesis

View File

@ -285,6 +285,7 @@ pub async fn mine_block_internal(
let new_block = Block { let new_block = Block {
vrf_block, vrf_block,
transactions: vec![Transaction::Rewards(rewards_transaction)], transactions: vec![Transaction::Rewards(rewards_transaction)],
original_transactions: Vec::new(),
}; };
// Return only candidates that pass local verification. // Return only candidates that pass local verification.

View File

@ -1,36 +1,37 @@
use super::*; use super::*;
pub async fn signature_exists(signature: &str, hash: &str) -> Result<bool> { pub async fn exact_transaction_exists(signature: &str, original: &[u8]) -> Result<bool> {
let client_handle = db_client().await?; let client_handle = db_client().await?;
let client = client_handle.as_ref(); let client = client_handle.as_ref();
// Check every mempool table because the signature column names differ by // Use the indexed signature to locate a candidate row, then compare the
// transaction type, especially for two-party swaps and loans. // complete serialized transaction so every byte must match what was
// previously validated and admitted to the mempool.
let row = client let row = client
.query_one( .query_one(
r#" r#"
SELECT SELECT
CASE CASE
WHEN EXISTS (SELECT 1 FROM transfer a WHERE a.signature = $1 AND a.hash = $2 AND a.processed = false) WHEN EXISTS (SELECT 1 FROM transfer a WHERE a.signature = $1 AND a.original = $2 AND a.processed = false)
OR EXISTS (SELECT 1 FROM token b WHERE b.signature = $1 AND b.hash = $2 AND b.processed = false) OR EXISTS (SELECT 1 FROM token b WHERE b.signature = $1 AND b.original = $2 AND b.processed = false)
OR EXISTS (SELECT 1 FROM issue_token c WHERE c.signature = $1 AND c.hash = $2 AND c.processed = false) OR EXISTS (SELECT 1 FROM issue_token c WHERE c.signature = $1 AND c.original = $2 AND c.processed = false)
OR EXISTS (SELECT 1 FROM burn d WHERE d.signature = $1 AND d.hash = $2 AND d.processed = false) OR EXISTS (SELECT 1 FROM burn d WHERE d.signature = $1 AND d.original = $2 AND d.processed = false)
OR EXISTS (SELECT 1 FROM nft e WHERE e.signature = $1 AND e.hash = $2 AND e.processed = false) OR EXISTS (SELECT 1 FROM nft e WHERE e.signature = $1 AND e.original = $2 AND e.processed = false)
OR EXISTS (SELECT 1 FROM marketing f WHERE f.signature = $1 AND f.hash = $2 AND f.processed = false) OR EXISTS (SELECT 1 FROM marketing f WHERE f.signature = $1 AND f.original = $2 AND f.processed = false)
OR EXISTS (SELECT 1 FROM vanity_address va WHERE va.signature = $1 AND va.hash = $2 AND va.processed = false) OR EXISTS (SELECT 1 FROM vanity_address va WHERE va.signature = $1 AND va.original = $2 AND va.processed = false)
OR EXISTS (SELECT 1 FROM swap g WHERE g.signature1 = $1 AND g.hash = $2 AND g.processed = false) OR EXISTS (SELECT 1 FROM swap g WHERE g.signature1 = $1 AND g.original = $2 AND g.processed = false)
OR EXISTS (SELECT 1 FROM swap h WHERE h.signature2 = $1 AND h.hash = $2 AND h.processed = false) OR EXISTS (SELECT 1 FROM swap h WHERE h.signature2 = $1 AND h.original = $2 AND h.processed = false)
OR EXISTS (SELECT 1 FROM loan_contract i WHERE i.signature1 = $1 AND i.hash = $2 AND i.processed = false) OR EXISTS (SELECT 1 FROM loan_contract i WHERE i.signature1 = $1 AND i.original = $2 AND i.processed = false)
OR EXISTS (SELECT 1 FROM loan_contract j WHERE j.signature2 = $1 AND j.hash = $2 AND j.processed = false) OR EXISTS (SELECT 1 FROM loan_contract j WHERE j.signature2 = $1 AND j.original = $2 AND j.processed = false)
OR EXISTS (SELECT 1 FROM loan_payment k WHERE k.signature = $1 AND k.hash = $2 AND k.processed = false) OR EXISTS (SELECT 1 FROM loan_payment k WHERE k.signature = $1 AND k.original = $2 AND k.processed = false)
OR EXISTS (SELECT 1 FROM collateral_claim l WHERE l.signature = $1 AND l.hash = $2 AND l.processed = false) OR EXISTS (SELECT 1 FROM collateral_claim l WHERE l.signature = $1 AND l.original = $2 AND l.processed = false)
OR EXISTS (SELECT 1 FROM storage_key sk WHERE sk.signature = $1 AND sk.hash = $2 AND sk.processed = false) OR EXISTS (SELECT 1 FROM storage_key sk WHERE sk.signature = $1 AND sk.original = $2 AND sk.processed = false)
OR EXISTS (SELECT 1 FROM storage_value sv WHERE sv.signature = $1 AND sv.hash = $2 AND sv.processed = false) OR EXISTS (SELECT 1 FROM storage_value sv WHERE sv.signature = $1 AND sv.original = $2 AND sv.processed = false)
THEN 1 THEN 1
ELSE 0 ELSE 0
END AS signature_found; END AS signature_found;
"#, "#,
&[&signature, &hash], &[&signature, &original],
) )
.await?; .await?;

View File

@ -225,9 +225,10 @@ mod schema;
mod selection; mod selection;
pub use lookups::{ pub use lookups::{
get_basecoin_balance, get_coin_balance, get_pending_payments_for_contract, largest_fee, exact_transaction_exists, get_basecoin_balance, get_coin_balance,
latest_pending_txids_by_address, pending_transaction_by_txid, signature_exists, get_pending_payments_for_contract, largest_fee, latest_pending_txids_by_address,
total_transactions, transaction_by_signature, transactions_by_address, pending_transaction_by_txid, total_transactions, transaction_by_signature,
transactions_by_address,
}; };
pub use processing::{ pub use processing::{
delete_by_signatures, delete_unprocessed_by_address, mark_processed_by_signatures, delete_by_signatures, delete_unprocessed_by_address, mark_processed_by_signatures,

View File

@ -64,10 +64,12 @@ pub async fn load_block_from_binary(binary_data: &[u8]) -> Result<Block, String>
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let mut i = VRF_BLOCK_BYTES; let mut i = VRF_BLOCK_BYTES;
let mut transactions: Vec<Transaction> = Vec::new(); let mut transactions: Vec<Transaction> = Vec::new();
let mut original_transactions = Vec::new();
while i < binary_data.len() { while i < binary_data.len() {
// Each transaction starts with its type byte, followed by the fixed-length body // Each transaction starts with its type byte, followed by the fixed-length body
// defined for that transaction family. // defined for that transaction family.
let transaction_start = i;
let txtype = binary_data[i]; let txtype = binary_data[i];
i += 1; i += 1;
let body_len = transaction_body_len(txtype)?; let body_len = transaction_body_len(txtype)?;
@ -332,11 +334,13 @@ pub async fn load_block_from_binary(binary_data: &[u8]) -> Result<Block, String>
} }
}; };
transactions.push(transaction); transactions.push(transaction);
original_transactions.push(binary_data[transaction_start..i].to_vec());
} }
let block = Block { let block = Block {
vrf_block, vrf_block,
transactions, transactions,
original_transactions,
}; };
Ok(block) Ok(block)

View File

@ -92,10 +92,12 @@ pub async fn load_block(block_number: u32) -> Result<Block, String> {
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let mut i = VRF_BLOCK_BYTES; let mut i = VRF_BLOCK_BYTES;
let mut transactions: Vec<Transaction> = Vec::new(); let mut transactions: Vec<Transaction> = Vec::new();
let mut original_transactions = Vec::new();
while i < binary_data.len() { while i < binary_data.len() {
// Each stored transaction begins with its type byte, followed by the fixed-size // Each stored transaction begins with its type byte, followed by the fixed-size
// body for that transaction family. // body for that transaction family.
let transaction_start = i;
let txtype = binary_data[i]; let txtype = binary_data[i];
i += 1; i += 1;
let body_len = transaction_body_len(txtype)?; let body_len = transaction_body_len(txtype)?;
@ -349,11 +351,13 @@ pub async fn load_block(block_number: u32) -> Result<Block, String> {
} }
}; };
transactions.push(transaction); transactions.push(transaction);
original_transactions.push(binary_data[transaction_start..i].to_vec());
} }
let block = Block { let block = Block {
vrf_block, vrf_block,
transactions, transactions,
original_transactions,
}; };
Ok(block) Ok(block)

View File

@ -22,13 +22,16 @@ use crate::common::types::{
STORAGE_STRING_TYPE, STORAGE_U128_TYPE, STORAGE_U16_TYPE, STORAGE_U32_TYPE, STORAGE_U64_TYPE, STORAGE_STRING_TYPE, STORAGE_U128_TYPE, STORAGE_U16_TYPE, STORAGE_U32_TYPE, STORAGE_U64_TYPE,
STORAGE_U8_TYPE, SWAP_TYPE, TRANSFER_TYPE, VANITY_ADDRESS_TYPE, STORAGE_U8_TYPE, SWAP_TYPE, TRANSFER_TYPE, VANITY_ADDRESS_TYPE,
}; };
use crate::encode;
use crate::records::memory::connections::get_client_type_from_memory; use crate::records::memory::connections::get_client_type_from_memory;
use crate::records::memory::enums::ClientType; use crate::records::memory::enums::ClientType;
use crate::records::memory::mempool::exact_transaction_exists;
use crate::records::memory::response_channels::{reserve_transient_entry_with_context, Command}; use crate::records::memory::response_channels::{reserve_transient_entry_with_context, Command};
use crate::rpc::command_maps::RPC_SUBMIT_TRANSACTION; use crate::rpc::command_maps::RPC_SUBMIT_TRANSACTION;
use crate::rpc::responses::RpcResponse; use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;
use crate::torrent::torrenting_system::get_nodes::get_live_node_broadcast_peers; use crate::torrent::torrenting_system::get_nodes::get_live_node_broadcast_peers;
use crate::wallets::structures::Wallet;
use crate::Arc; use crate::Arc;
use crate::Mutex; use crate::Mutex;
use crate::OnceLock; use crate::OnceLock;
@ -124,6 +127,27 @@ pub async fn save_and_submit(
// the declared transaction type. // the declared transaction type.
// In each branch, a non-empty verifier response means the transaction // In each branch, a non-empty verifier response means the transaction
// already exists in the mempool and should not be rebroadcast. // already exists in the mempool and should not be rebroadcast.
// All submitted transaction families end with their final signature.
// Use that indexed signature to locate a candidate mempool row, then
// require the complete original bytes to match before reporting a duplicate.
if tx.len() >= Wallet::SIGNATURE_LENGTH {
let signature = encode(&tx[tx.len() - Wallet::SIGNATURE_LENGTH..]);
let mut original = Vec::with_capacity(1 + tx.len());
original.push(txtype);
original.extend_from_slice(&tx);
match exact_transaction_exists(&signature, &original).await {
Ok(true) => {
return RpcResponse::Binary(
b"successful_broadcast: false already_in_mempool".to_vec(),
);
}
Ok(false) => {}
Err(err) => {
return RpcResponse::Binary(format!("error: {err}").into_bytes());
}
}
}
match txtype { match txtype {
TRANSFER_TYPE => match TransferTransaction::from_bytes(txtype, &tx).await { TRANSFER_TYPE => match TransferTransaction::from_bytes(txtype, &tx).await {
Ok(transfer) => match transfer.verify(db).await { Ok(transfer) => match transfer.verify(db).await {

View File

@ -4,9 +4,7 @@ use crate::common::nft_assets::nft_asset_name;
use crate::common::types::Transaction; use crate::common::types::Transaction;
use crate::decode; use crate::decode;
use crate::records::balance_sheet::get_wallet_balance::get_balance_with_db; use crate::records::balance_sheet::get_wallet_balance::get_balance_with_db;
use crate::records::memory::mempool::{ use crate::records::memory::mempool::{get_basecoin_balance, get_coin_balance, BASECOIN};
get_basecoin_balance, get_coin_balance, signature_exists, BASECOIN,
};
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid; use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse; use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;
@ -172,23 +170,6 @@ fn add_amount_to_total(
Ok(()) Ok(())
} }
async fn transaction_is_pending(view: &TransactionBalanceView) -> Result<bool, String> {
for signature in &view.signatures {
if signature.is_empty() {
continue;
}
if signature_exists(signature, &view.hash)
.await
.map_err(|err| err.to_string())?
{
return Ok(true);
}
}
Ok(false)
}
async fn loan_contract_for_hash( async fn loan_contract_for_hash(
db: &Db, db: &Db,
contract_hash: &str, contract_hash: &str,
@ -678,13 +659,13 @@ async fn pending_reserved_amount(db: &Db, address: &str, coin: &str) -> Result<u
pub(crate) async fn prepare_transaction_balance_reservation( pub(crate) async fn prepare_transaction_balance_reservation(
db: &Db, db: &Db,
transaction: &Transaction, transaction: &Transaction,
already_reserved_in_mempool: bool,
) -> Result<TransactionBalanceReservation, String> { ) -> Result<TransactionBalanceReservation, String> {
let view = transaction_balance_view(db, transaction).await?; let view = transaction_balance_view(db, transaction).await?;
if view.check_saved_txid && saved_txid_exists(db, &view.hash)? { if view.check_saved_txid && saved_txid_exists(db, &view.hash)? {
return Err("This transaction already exists.".to_string()); return Err("This transaction already exists.".to_string());
} }
let already_reserved_in_mempool = transaction_is_pending(&view).await?;
let loan_payment = if let Transaction::Borrower(tx) = transaction { let loan_payment = if let Transaction::Borrower(tx) = transaction {
let payment = &tx.unsigned_contract_payment; let payment = &tx.unsigned_contract_payment;
let contract = loan_contract_for_hash(db, &payment.contract_hash).await?; let contract = loan_contract_for_hash(db, &payment.contract_hash).await?;

View File

@ -1,6 +1,5 @@
// These lower-level checks are reused by the higher-level async verification routines. // These lower-level checks are reused by the higher-level async verification routines.
pub mod balance_check; pub mod balance_check;
pub mod block_balance; pub mod block_balance;
pub mod mempool_check;
pub mod time_checks; pub mod time_checks;
pub mod verify_db; pub mod verify_db;

View File

@ -1,9 +1,11 @@
use crate::common::types::Transaction; use crate::common::types::Transaction;
use crate::records::memory::mempool::exact_transaction_exists;
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::block_balance::{ use crate::verifications::async_funcs::checks::block_balance::{
prepare_transaction_balance_reservation, BlockBalanceTracker, prepare_transaction_balance_reservation, BlockBalanceTracker,
}; };
use crate::verifications::sync_funcs::transaction_verify_loop::COUNTER; use crate::verifications::sync_funcs::transaction_verify_loop::COUNTER;
use crate::verifications::verification_types::VerifiableTransaction;
use crate::Arc; use crate::Arc;
use crate::AtomicBool; use crate::AtomicBool;
use crate::AtomicOrdering; use crate::AtomicOrdering;
@ -11,7 +13,7 @@ use crate::Mutex;
pub async fn verify_transactions( pub async fn verify_transactions(
miner: String, miner: String,
transactions: Vec<Transaction>, transactions: Vec<VerifiableTransaction>,
db: &Db, db: &Db,
stop_flag: Arc<AtomicBool>, stop_flag: Arc<AtomicBool>,
balance_tracker: Arc<Mutex<BlockBalanceTracker>>, balance_tracker: Arc<Mutex<BlockBalanceTracker>>,
@ -78,15 +80,41 @@ pub async fn verify_transactions(
// normalize failures into a consistent error string. // normalize failures into a consistent error string.
async fn verify_transaction( async fn verify_transaction(
miner: String, miner: String,
transaction: Transaction, verifiable: VerifiableTransaction,
db: &Db, db: &Db,
balance_tracker: Arc<Mutex<BlockBalanceTracker>>, balance_tracker: Arc<Mutex<BlockBalanceTracker>>,
block_timestamp: u32, block_timestamp: u32,
) -> Result<String, String> { ) -> Result<String, String> {
let transaction = verifiable.transaction;
let already_in_mempool = match (
transaction.mempool_signature(),
verifiable.original.as_deref(),
) {
(Some(signature), Some(original)) if !signature.is_empty() => {
exact_transaction_exists(signature, original)
.await
.map_err(|err| err.to_string())?
}
_ => false,
};
// Exact mempool bytes prove ordinary transactions already passed their
// complete type-specific validation. Collateral claims remain time-aware
// and must recheck their schedule against the containing block timestamp.
if already_in_mempool && !matches!(&transaction, Transaction::Collateral(_)) {
let signature = transaction
.mempool_signature()
.unwrap_or_default()
.to_string();
reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool).await?;
return Ok(signature);
}
if let Transaction::Genesis(genesis_tx) = &transaction { if let Transaction::Genesis(genesis_tx) = &transaction {
match genesis_tx.verify().await { match genesis_tx.verify().await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -105,7 +133,13 @@ async fn verify_transaction(
"You cannot include more than 1 reward in a mined block.".to_string() "You cannot include more than 1 reward in a mined block.".to_string()
); );
} else { } else {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(
db,
&transaction,
balance_tracker,
already_in_mempool,
)
.await?;
return Ok(value); return Ok(value);
} }
} }
@ -117,7 +151,8 @@ async fn verify_transaction(
if let Transaction::Transfer(transfer_tx) = &transaction { if let Transaction::Transfer(transfer_tx) = &transaction {
match transfer_tx.verify(db).await { match transfer_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -128,7 +163,8 @@ async fn verify_transaction(
if let Transaction::Burn(burn_tx) = &transaction { if let Transaction::Burn(burn_tx) = &transaction {
match burn_tx.verify(db).await { match burn_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -139,7 +175,8 @@ async fn verify_transaction(
if let Transaction::Token(create_token_tx) = &transaction { if let Transaction::Token(create_token_tx) = &transaction {
match create_token_tx.verify(db).await { match create_token_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -152,7 +189,8 @@ async fn verify_transaction(
if let Transaction::IssueToken(issue_token_tx) = &transaction { if let Transaction::IssueToken(issue_token_tx) = &transaction {
match issue_token_tx.verify(db).await { match issue_token_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -165,7 +203,8 @@ async fn verify_transaction(
if let Transaction::Nft(create_nft_tx) = &transaction { if let Transaction::Nft(create_nft_tx) = &transaction {
match create_nft_tx.verify(db).await { match create_nft_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -178,7 +217,8 @@ async fn verify_transaction(
if let Transaction::Marketing(marketing_tx) = &transaction { if let Transaction::Marketing(marketing_tx) = &transaction {
match marketing_tx.verify(db).await { match marketing_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -191,7 +231,8 @@ async fn verify_transaction(
if let Transaction::Swap(swap_tx) = &transaction { if let Transaction::Swap(swap_tx) = &transaction {
match swap_tx.verify(db).await { match swap_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -202,7 +243,8 @@ async fn verify_transaction(
if let Transaction::Lender(loan_creation_tx) = &transaction { if let Transaction::Lender(loan_creation_tx) = &transaction {
match loan_creation_tx.verify(db).await { match loan_creation_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -215,7 +257,8 @@ async fn verify_transaction(
if let Transaction::Borrower(loan_payment_tx) = &transaction { if let Transaction::Borrower(loan_payment_tx) = &transaction {
match loan_payment_tx.verify_for_block(db).await { match loan_payment_tx.verify_for_block(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -226,9 +269,13 @@ async fn verify_transaction(
} }
} }
if let Transaction::Collateral(collateral_claim_tx) = &transaction { if let Transaction::Collateral(collateral_claim_tx) = &transaction {
match collateral_claim_tx.verify_at(db, block_timestamp).await { match collateral_claim_tx
.verify_at(db, block_timestamp, already_in_mempool)
.await
{
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -241,7 +288,8 @@ async fn verify_transaction(
if let Transaction::Vanity(vanity_tx) = &transaction { if let Transaction::Vanity(vanity_tx) = &transaction {
match vanity_tx.verify(db).await { match vanity_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -254,7 +302,8 @@ async fn verify_transaction(
if let Transaction::StorageKey(storage_key_tx) = &transaction { if let Transaction::StorageKey(storage_key_tx) = &transaction {
match storage_key_tx.verify(db).await { match storage_key_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -267,7 +316,8 @@ async fn verify_transaction(
if let Transaction::StorageBool(storage_tx) = &transaction { if let Transaction::StorageBool(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -280,7 +330,8 @@ async fn verify_transaction(
if let Transaction::StorageU8(storage_tx) = &transaction { if let Transaction::StorageU8(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -293,7 +344,8 @@ async fn verify_transaction(
if let Transaction::StorageU16(storage_tx) = &transaction { if let Transaction::StorageU16(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -306,7 +358,8 @@ async fn verify_transaction(
if let Transaction::StorageU32(storage_tx) = &transaction { if let Transaction::StorageU32(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -319,7 +372,8 @@ async fn verify_transaction(
if let Transaction::StorageU64(storage_tx) = &transaction { if let Transaction::StorageU64(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -332,7 +386,8 @@ async fn verify_transaction(
if let Transaction::StorageU128(storage_tx) = &transaction { if let Transaction::StorageU128(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -345,7 +400,8 @@ async fn verify_transaction(
if let Transaction::StorageString(storage_tx) = &transaction { if let Transaction::StorageString(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -358,7 +414,8 @@ async fn verify_transaction(
if let Transaction::StorageI8(storage_tx) = &transaction { if let Transaction::StorageI8(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -371,7 +428,8 @@ async fn verify_transaction(
if let Transaction::StorageI16(storage_tx) = &transaction { if let Transaction::StorageI16(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -384,7 +442,8 @@ async fn verify_transaction(
if let Transaction::StorageI32(storage_tx) = &transaction { if let Transaction::StorageI32(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -397,7 +456,8 @@ async fn verify_transaction(
if let Transaction::StorageI64(storage_tx) = &transaction { if let Transaction::StorageI64(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -410,7 +470,8 @@ async fn verify_transaction(
if let Transaction::StorageI128(storage_tx) = &transaction { if let Transaction::StorageI128(storage_tx) = &transaction {
match storage_tx.verify(db).await { match storage_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -423,7 +484,8 @@ async fn verify_transaction(
if let Transaction::DeleteKey(delete_key_tx) = &transaction { if let Transaction::DeleteKey(delete_key_tx) = &transaction {
match delete_key_tx.verify(db).await { match delete_key_tx.verify(db).await {
Ok(value) => { Ok(value) => {
reserve_verified_transaction(db, &transaction, balance_tracker).await?; reserve_verified_transaction(db, &transaction, balance_tracker, already_in_mempool)
.await?;
return Ok(value); return Ok(value);
} }
Err(err) => { Err(err) => {
@ -443,10 +505,12 @@ async fn reserve_verified_transaction(
db: &Db, db: &Db,
transaction: &Transaction, transaction: &Transaction,
balance_tracker: Arc<Mutex<BlockBalanceTracker>>, balance_tracker: Arc<Mutex<BlockBalanceTracker>>,
already_in_mempool: bool,
) -> Result<(), String> { ) -> Result<(), String> {
// Build the balance reservation while this worker still has the // Build the balance reservation while this worker still has the
// transaction in hand, then lock only the shared in-block totals. // transaction in hand, then lock only the shared in-block totals.
let reservation = prepare_transaction_balance_reservation(db, transaction).await?; let reservation =
prepare_transaction_balance_reservation(db, transaction, already_in_mempool).await?;
let mut tracker = balance_tracker.lock().await; let mut tracker = balance_tracker.lock().await;
tracker.reserve(reservation) tracker.reserve(reservation)
} }

View File

@ -1,7 +1,6 @@
use crate::blocks::block::{Block, UnminedBlock}; use crate::blocks::block::{Block, UnminedBlock};
use crate::common::binary_conversions::hex_to_u64; use crate::common::binary_conversions::hex_to_u64;
use crate::common::check_genesis::genesis_checkup; use crate::common::check_genesis::genesis_checkup;
use crate::common::types::Transaction;
use crate::encode; use crate::encode;
use crate::miner::fairness::fairness_difficulty; use crate::miner::fairness::fairness_difficulty;
use crate::records::block_height::get_block_height::get_height; use crate::records::block_height::get_block_height::get_height;
@ -13,6 +12,7 @@ use crate::records::unpack_block::unpack_header::load_block_header;
use crate::records::wallet_registry::resolve_pubkey_from_short_address; use crate::records::wallet_registry::resolve_pubkey_from_short_address;
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::verification_service::VerificationService; use crate::verifications::verification_service::VerificationService;
use crate::verifications::verification_types::VerifiableTransaction;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
use crate::Arc; use crate::Arc;
use crate::Utc; use crate::Utc;
@ -97,9 +97,20 @@ impl Block {
.await?; .await?;
} }
let transactions = self
.transactions
.iter()
.cloned()
.enumerate()
.map(|(index, transaction)| VerifiableTransaction {
transaction,
original: self.original_transactions.get(index).cloned(),
})
.collect();
let results = Self::send_transactions_to_validate( let results = Self::send_transactions_to_validate(
miner.to_string(), miner.to_string(),
self.transactions.clone(), transactions,
db.clone(), db.clone(),
verification_service, verification_service,
timestamp, timestamp,
@ -181,7 +192,7 @@ impl Block {
async fn send_transactions_to_validate( async fn send_transactions_to_validate(
miner: String, miner: String,
transactions: Vec<Transaction>, transactions: Vec<VerifiableTransaction>,
db: crate::sled::Db, db: crate::sled::Db,
verification_service: Arc<VerificationService>, verification_service: Arc<VerificationService>,
block_timestamp: u32, block_timestamp: u32,

View File

@ -9,7 +9,6 @@ use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse; use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::db_hex_verification; use crate::verifications::async_funcs::checks::verify_db::db_hex_verification;
use crate::verifications::async_funcs::total_payments::get_total_payments; use crate::verifications::async_funcs::total_payments::get_total_payments;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
@ -32,12 +31,6 @@ impl ContractPaymentTransaction {
let hash = self.unsigned_contract_payment.hash().await; let hash = self.unsigned_contract_payment.hash().await;
let signature = &self.signature; let signature = &self.signature;
// Transactions already present in the mempool can short-circuit
// the deeper verification path and reuse their stored signature.
if memcheck(signature, &hash).await {
return Ok(signature.to_string());
}
// Loan payments must come from a valid wallet address and carry a // Loan payments must come from a valid wallet address and carry a
// valid signature from the payer. // valid signature from the payer.
let address = &self.unsigned_contract_payment.address; let address = &self.unsigned_contract_payment.address;

View File

@ -9,7 +9,6 @@ use crate::records::wallet_registry::{
}; };
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::{ use crate::verifications::async_funcs::checks::verify_db::{
db_bytes_verification, db_hex_verification, db_bytes_verification, db_hex_verification,
}; };
@ -21,12 +20,6 @@ impl BurnTransaction {
pub async fn verify(&self, db: &Db) -> Result<String, String> { pub async fn verify(&self, db: &Db) -> Result<String, String> {
let hash = self.unsigned_burn.hash().await; let hash = self.unsigned_burn.hash().await;
// Transactions already present in the mempool can short-circuit
// the deeper verification path and reuse their stored signature.
if memcheck(&self.signature, &hash).await {
return Ok(self.signature.clone());
}
// Burn transactions are anchored to the sender wallet and the // Burn transactions are anchored to the sender wallet and the
// signed burn payload before any asset-specific rules apply. // signed burn payload before any asset-specific rules apply.
if !Wallet::short_address_validation(&self.unsigned_burn.address) { if !Wallet::short_address_validation(&self.unsigned_burn.address) {

View File

@ -10,7 +10,6 @@ use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
use crate::rpc::responses::RpcResponse; use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::db_hex_verification; use crate::verifications::async_funcs::checks::verify_db::db_hex_verification;
use crate::verifications::async_funcs::total_payments::get_total_payments; use crate::verifications::async_funcs::total_payments::get_total_payments;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
@ -19,19 +18,25 @@ use crate::{decode, encode};
impl CollateralClaimTransaction { impl CollateralClaimTransaction {
pub async fn verify(&self, db: &Db) -> Result<String, String> { pub async fn verify(&self, db: &Db) -> Result<String, String> {
self.verify_internal(db, Utc::now().timestamp() as u32, true) self.verify_internal(db, Utc::now().timestamp() as u32, false)
.await .await
} }
pub async fn verify_at(&self, db: &Db, as_of_timestamp: u32) -> Result<String, String> { pub async fn verify_at(
self.verify_internal(db, as_of_timestamp, false).await &self,
db: &Db,
as_of_timestamp: u32,
already_in_mempool: bool,
) -> Result<String, String> {
self.verify_internal(db, as_of_timestamp, already_in_mempool)
.await
} }
async fn verify_internal( async fn verify_internal(
&self, &self,
db: &Db, db: &Db,
as_of_timestamp: u32, as_of_timestamp: u32,
allow_mempool_short_circuit: bool, already_in_mempool: bool,
) -> Result<String, String> { ) -> Result<String, String> {
let hash = self.unsigned_collateral_claim.hash().await; let hash = self.unsigned_collateral_claim.hash().await;
let signature = &self.signature; let signature = &self.signature;
@ -42,13 +47,6 @@ impl CollateralClaimTransaction {
); );
} }
let already_in_mempool = memcheck(signature, &hash).await;
// Submission can reuse an already verified mempool entry. Block
// validation still checks the schedule against the block timestamp.
if allow_mempool_short_circuit && already_in_mempool {
return Ok(signature.to_string());
}
// Collateral claims are valid only when the claimant signs the // Collateral claims are valid only when the claimant signs the
// claim transaction with the same wallet in the payload. // claim transaction with the same wallet in the payload.
if !Wallet::short_address_validation(&self.unsigned_collateral_claim.address) { if !Wallet::short_address_validation(&self.unsigned_collateral_claim.address) {

View File

@ -10,7 +10,6 @@ use crate::records::wallet_registry::{
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::asset_rules::is_reserved_base_coin_ticker; use crate::verifications::async_funcs::asset_rules::is_reserved_base_coin_ticker;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::db_hex_verification; use crate::verifications::async_funcs::checks::verify_db::db_hex_verification;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
use crate::Cid; use crate::Cid;
@ -38,12 +37,6 @@ impl CreateNftTransaction {
return Err("NFT name is reserved for the base coin.".to_string()); return Err("NFT name is reserved for the base coin.".to_string());
} }
// Transactions already present in the mempool can short-circuit
// the deeper verification path and reuse their stored signature.
if memcheck(signature, hash).await {
return Ok(signature.to_string());
}
// NFT creation is anchored to a valid creator wallet and a valid // NFT creation is anchored to a valid creator wallet and a valid
// signature over the unsigned NFT payload. // signature over the unsigned NFT payload.
let creator = &self.unsigned_create_nft.creator; let creator = &self.unsigned_create_nft.creator;

View File

@ -9,7 +9,6 @@ use crate::records::wallet_registry::{
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::asset_rules::is_reserved_base_coin_ticker; use crate::verifications::async_funcs::asset_rules::is_reserved_base_coin_ticker;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::{ use crate::verifications::async_funcs::checks::verify_db::{
db_bytes_verification, db_hex_verification, db_bytes_verification, db_hex_verification,
}; };
@ -17,10 +16,7 @@ use crate::wallets::structures::Wallet;
impl CreateTokenTransaction { impl CreateTokenTransaction {
pub async fn verify(&self, db: &Db) -> Result<String, String> { pub async fn verify(&self, db: &Db) -> Result<String, String> {
// Transactions already present in the mempool can short-circuit
// the deeper verification path and reuse their stored signature.
let hash = self.unsigned_create_token.hash().await; let hash = self.unsigned_create_token.hash().await;
let signature = &self.signature;
// Token tickers use the fixed padded coin-length format. // Token tickers use the fixed padded coin-length format.
let ticker_length = self.unsigned_create_token.ticker.len(); let ticker_length = self.unsigned_create_token.ticker.len();
@ -38,10 +34,6 @@ impl CreateTokenTransaction {
return Err("Ticker is reserved for the base coin.".to_string()); return Err("Ticker is reserved for the base coin.".to_string());
} }
if memcheck(signature, &hash).await {
return Ok(self.signature.clone());
}
// Token creation is anchored to the creator's wallet signature. // Token creation is anchored to the creator's wallet signature.
if !Wallet::short_address_validation(&self.unsigned_create_token.creator) { if !Wallet::short_address_validation(&self.unsigned_create_token.creator) {
return Err("Sender Wallet Address is Invalid.".to_string()); return Err("Sender Wallet Address is Invalid.".to_string());

View File

@ -6,7 +6,6 @@ use crate::records::wallet_registry::{
require_canonical_registered_short_address, resolve_pubkey_from_short_address, require_canonical_registered_short_address, resolve_pubkey_from_short_address,
}; };
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::db_hex_verification; use crate::verifications::async_funcs::checks::verify_db::db_hex_verification;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
use crate::{decode, encode, sled::Db}; use crate::{decode, encode, sled::Db};
@ -16,10 +15,6 @@ impl DeleteKey {
let delete = &self.unsigned_deletekey; let delete = &self.unsigned_deletekey;
let hash = delete.hash().await; let hash = delete.hash().await;
// Existing mempool entries short-circuit before the storage-state checks.
if memcheck(&self.signature, &hash).await {
return Ok(self.signature.clone());
}
if delete.txtype != DELETE_KEY_TYPE { if delete.txtype != DELETE_KEY_TYPE {
return Err("Delete-key transaction type is invalid.".to_string()); return Err("Delete-key transaction type is invalid.".to_string());
} }

View File

@ -12,7 +12,6 @@ use crate::rpc::responses::RpcResponse;
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::asset_rules::reserved_base_coins; use crate::verifications::async_funcs::asset_rules::reserved_base_coins;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::{ use crate::verifications::async_funcs::checks::verify_db::{
db_bytes_verification, db_hex_verification, db_bytes_verification, db_hex_verification,
}; };
@ -50,13 +49,6 @@ fn token_hard_limit(db: &Db, ticker: &str) -> Option<u8> {
impl IssueTokenTransaction { impl IssueTokenTransaction {
pub async fn verify(&self, db: &Db) -> Result<String, String> { pub async fn verify(&self, db: &Db) -> Result<String, String> {
let hash = self.unsigned_issue_token.hash().await; let hash = self.unsigned_issue_token.hash().await;
let signature = &self.signature;
// Transactions already present in the mempool can short-circuit
// the deeper verification path and reuse their stored signature.
if memcheck(signature, &hash).await {
return Ok(self.signature.clone());
}
// Issuance is anchored to the creator's wallet signature. // Issuance is anchored to the creator's wallet signature.
if !Wallet::short_address_validation(&self.unsigned_issue_token.creator) { if !Wallet::short_address_validation(&self.unsigned_issue_token.creator) {

View File

@ -11,7 +11,6 @@ use crate::records::wallet_registry::{
}; };
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::time_checks::is_within_30_days; use crate::verifications::async_funcs::checks::time_checks::is_within_30_days;
use crate::verifications::async_funcs::checks::verify_db::{ use crate::verifications::async_funcs::checks::verify_db::{
db_bytes_verification, db_hex_verification, db_bytes_verification, db_hex_verification,
@ -21,13 +20,6 @@ use crate::wallets::structures::Wallet;
impl LoanContractTransaction { impl LoanContractTransaction {
pub async fn verify(&self, db: &Db) -> Result<String, String> { pub async fn verify(&self, db: &Db) -> Result<String, String> {
let calculated_hash = &self.unsigned_loan_contract.hash().await; let calculated_hash = &self.unsigned_loan_contract.hash().await;
let signature2 = &self.signature2;
// Transactions already present in the mempool can short-circuit
// the deeper verification path and reuse their stored signature.
if memcheck(signature2, calculated_hash).await {
return Ok(signature2.to_string());
};
// Loan contracts require valid lender and borrower wallet addresses. // Loan contracts require valid lender and borrower wallet addresses.
let lender = &self.unsigned_loan_contract.lender; let lender = &self.unsigned_loan_contract.lender;

View File

@ -7,20 +7,13 @@ use crate::records::wallet_registry::{
}; };
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::db_hex_verification; use crate::verifications::async_funcs::checks::verify_db::db_hex_verification;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
impl MarketingTransaction { impl MarketingTransaction {
pub async fn verify(&self, db: &Db) -> Result<String, String> { pub async fn verify(&self, db: &Db) -> Result<String, String> {
// Transactions already present in the mempool can short-circuit
// the deeper verification path and reuse their stored signature.
let hash = &self.unsigned_marketing.hash().await; let hash = &self.unsigned_marketing.hash().await;
let signature = &self.signature; let signature = &self.signature;
if memcheck(signature, hash).await {
return Ok(signature.to_string());
}
// Marketing transactions must come from a valid advertiser wallet // Marketing transactions must come from a valid advertiser wallet
// and carry a valid signature from that same wallet. // and carry a valid signature from that same wallet.
let advertiser = &self.unsigned_marketing.advertiser; let advertiser = &self.unsigned_marketing.advertiser;

View File

@ -7,7 +7,6 @@ use crate::records::wallet_registry::{
}; };
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::db_hex_verification; use crate::verifications::async_funcs::checks::verify_db::db_hex_verification;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
@ -21,13 +20,7 @@ impl StorageKey {
return Err("Storage key transaction type is invalid.".to_string()); return Err("Storage key transaction type is invalid.".to_string());
} }
// Transactions already present in the mempool can short-circuit
// deeper verification and return their stored signature.
let hash = storage_key.hash().await; let hash = storage_key.hash().await;
if memcheck(&self.signature, &hash).await {
return Ok(self.signature.clone());
}
// Only canonical registered short addresses can create storage // Only canonical registered short addresses can create storage
// keys. Vanity aliases are resolved by callers before they reach // keys. Vanity aliases are resolved by callers before they reach
// this fixed transaction format. // this fixed transaction format.

View File

@ -17,7 +17,6 @@ use crate::records::wallet_registry::{
}; };
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::db_hex_verification; use crate::verifications::async_funcs::checks::verify_db::db_hex_verification;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
@ -32,13 +31,6 @@ async fn verify_storage_value<T: StorageValueTransaction>(
// fee, hash, and signature. // fee, hash, and signature.
let parts = tx.storage_parts(); let parts = tx.storage_parts();
// Match the rest of transaction verification: if this exact signed
// transaction is already in the mempool, return before doing deeper
// database checks.
if memcheck(parts.signature, &parts.hash).await {
return Ok(parts.signature.to_string());
}
// The shared verifier is called by each concrete storage type, so // The shared verifier is called by each concrete storage type, so
// confirm the embedded transaction type matches that caller. // confirm the embedded transaction type matches that caller.
if parts.txtype != expected_type { if parts.txtype != expected_type {
@ -187,14 +179,6 @@ impl StorageString {
pub async fn verify(&self, db: &Db) -> Result<String, String> { pub async fn verify(&self, db: &Db) -> Result<String, String> {
// String storage has extra chain-linking rules before it can use // String storage has extra chain-linking rules before it can use
// the common storage-value verifier. // the common storage-value verifier.
let parts = self.storage_parts();
// Existing mempool entries can still short-circuit before walking
// the previous-hash string chain.
if memcheck(parts.signature, &parts.hash).await {
return Ok(parts.signature.to_string());
}
// The on-chain string chunk is fixed at 180 bytes. Shorter text is // The on-chain string chunk is fixed at 180 bytes. Shorter text is
// padded by the creator and trimmed only when saved into sled. // padded by the creator and trimmed only when saved into sled.
if self.unsigned_storage.value.as_bytes().len() != STORAGE_STRING_VALUE_BYTES { if self.unsigned_storage.value.as_bytes().len() != STORAGE_STRING_VALUE_BYTES {

View File

@ -9,7 +9,6 @@ use crate::records::wallet_registry::{
}; };
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::time_checks::is_within_30_days; use crate::verifications::async_funcs::checks::time_checks::is_within_30_days;
use crate::verifications::async_funcs::checks::verify_db::{ use crate::verifications::async_funcs::checks::verify_db::{
db_bytes_verification, db_hex_verification, db_bytes_verification, db_hex_verification,
@ -36,13 +35,7 @@ fn validate_swap_tip(value: u64, tip: u64, is_nft: bool, sender: &str) -> Result
impl SwapTransaction { impl SwapTransaction {
pub async fn verify(&self, db: &Db) -> Result<String, String> { pub async fn verify(&self, db: &Db) -> Result<String, String> {
// Transactions already present in the mempool can short-circuit
// the deeper verification path and reuse their stored signature.
let hash = self.unsigned_swap.hash().await; let hash = self.unsigned_swap.hash().await;
if memcheck(&self.signature2, &hash).await {
return Ok(self.signature2.clone());
}
// Both swap participants must provide valid wallet addresses and // Both swap participants must provide valid wallet addresses and
// matching signatures over the shared unsigned swap payload. // matching signatures over the shared unsigned swap payload.
if !(Wallet::short_address_validation(&self.unsigned_swap.sender1) if !(Wallet::short_address_validation(&self.unsigned_swap.sender1)

View File

@ -10,7 +10,6 @@ use crate::records::wallet_registry::{
}; };
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::{ use crate::verifications::async_funcs::checks::verify_db::{
db_bytes_verification, db_hex_verification, db_bytes_verification, db_hex_verification,
}; };
@ -35,12 +34,6 @@ fn is_zero_burn_address(address: &str) -> bool {
impl TransferTransaction { impl TransferTransaction {
pub async fn verify(&self, db: &Db) -> Result<String, String> { pub async fn verify(&self, db: &Db) -> Result<String, String> {
let hash = self.unsigned_transfer.hash().await; let hash = self.unsigned_transfer.hash().await;
// Transactions that are already sitting in the mempool can skip
// the deeper verification work and return their stored signature.
if memcheck(&self.signature, &hash).await {
return Ok(self.signature.clone());
}
// Validate both wallet addresses and the signed transfer payload // Validate both wallet addresses and the signed transfer payload
// before checking balances and asset-specific rules. // before checking balances and asset-specific rules.
if !(Wallet::short_address_validation(&self.unsigned_transfer.sender) if !(Wallet::short_address_validation(&self.unsigned_transfer.sender)

View File

@ -7,7 +7,6 @@ use crate::records::wallet_registry::{
}; };
use crate::sled::Db; use crate::sled::Db;
use crate::verifications::async_funcs::checks::balance_check::balance_checkup; use crate::verifications::async_funcs::checks::balance_check::balance_checkup;
use crate::verifications::async_funcs::checks::mempool_check::memcheck;
use crate::verifications::async_funcs::checks::verify_db::db_hex_verification; use crate::verifications::async_funcs::checks::verify_db::db_hex_verification;
use crate::wallets::structures::Wallet; use crate::wallets::structures::Wallet;
@ -17,12 +16,6 @@ impl VanityAddressTransaction {
// address-registration payload, so recompute that hash first. // address-registration payload, so recompute that hash first.
let hash = self.unsigned_vanity_address.hash().await; let hash = self.unsigned_vanity_address.hash().await;
// Transactions already present in the mempool can short-circuit
// the deeper verification path and reuse their stored signature.
if memcheck(&self.signature, &hash).await {
return Ok(self.signature.clone());
}
// The paying wallet must already be a valid short address for the // The paying wallet must already be a valid short address for the
// current network before registry ownership is checked. // current network before registry ownership is checked.
let address = &self.unsigned_vanity_address.address; let address = &self.unsigned_vanity_address.address;

View File

@ -1,11 +1,10 @@
use crate::common::types::Transaction;
use crate::rayon::ThreadPool; use crate::rayon::ThreadPool;
use crate::rayon::ThreadPoolBuilder; use crate::rayon::ThreadPoolBuilder;
use crate::verifications::async_funcs::checks::block_balance::BlockBalanceTracker; use crate::verifications::async_funcs::checks::block_balance::BlockBalanceTracker;
use crate::verifications::async_funcs::transactions::verify_transactions; use crate::verifications::async_funcs::transactions::verify_transactions;
use crate::verifications::sync_funcs::transaction_verify_loop::COUNTER; use crate::verifications::sync_funcs::transaction_verify_loop::COUNTER;
use crate::verifications::verification_types::{ use crate::verifications::verification_types::{
VerificationChunkJob, VerificationChunkResult, VerificationJob, VerifiableTransaction, VerificationChunkJob, VerificationChunkResult, VerificationJob,
}; };
use crate::Arc; use crate::Arc;
use crate::AtomicBool; use crate::AtomicBool;
@ -75,7 +74,7 @@ impl VerificationService {
pub async fn verify_block_transactions( pub async fn verify_block_transactions(
&self, &self,
miner: String, miner: String,
transactions: Vec<Transaction>, transactions: Vec<VerifiableTransaction>,
db: crate::sled::Db, db: crate::sled::Db,
block_timestamp: u32, block_timestamp: u32,
) -> Result<Vec<String>, String> { ) -> Result<Vec<String>, String> {

View File

@ -1,10 +1,16 @@
use crate::common::types::Transaction; use crate::common::types::Transaction;
use crate::sled::Db; use crate::sled::Db;
#[derive(Clone)]
pub struct VerifiableTransaction {
pub transaction: Transaction,
pub original: Option<Vec<u8>>,
}
// VerificationJob represents one full transaction batch submitted to the verification service. // VerificationJob represents one full transaction batch submitted to the verification service.
pub struct VerificationJob { pub struct VerificationJob {
pub miner: String, pub miner: String,
pub transactions: Vec<Transaction>, pub transactions: Vec<VerifiableTransaction>,
pub db: Db, pub db: Db,
pub block_timestamp: u32, pub block_timestamp: u32,
} }
@ -12,7 +18,7 @@ pub struct VerificationJob {
// VerificationChunkJob is the per-worker slice created from a larger verification job. // VerificationChunkJob is the per-worker slice created from a larger verification job.
pub struct VerificationChunkJob { pub struct VerificationChunkJob {
pub miner: String, pub miner: String,
pub transactions: Vec<Transaction>, pub transactions: Vec<VerifiableTransaction>,
pub db: Db, pub db: Db,
pub block_timestamp: u32, pub block_timestamp: u32,
} }