2026-05-24 17:56:57 +00:00
|
|
|
use crate::blocks::burn::BurnTransaction;
|
|
|
|
|
use crate::blocks::collateral::CollateralClaimTransaction;
|
|
|
|
|
use crate::blocks::issue_token::IssueTokenTransaction;
|
|
|
|
|
use crate::blocks::loan_payment::ContractPaymentTransaction;
|
|
|
|
|
use crate::blocks::loans::LoanContractTransaction;
|
|
|
|
|
use crate::blocks::marketing::MarketingTransaction;
|
|
|
|
|
use crate::blocks::nft::CreateNftTransaction;
|
|
|
|
|
use crate::blocks::swap::SwapTransaction;
|
|
|
|
|
use crate::blocks::token::CreateTokenTransaction;
|
|
|
|
|
use crate::blocks::transfer::TransferTransaction;
|
|
|
|
|
use crate::blocks::vanity::VanityAddressTransaction;
|
2026-06-27 16:28:13 +00:00
|
|
|
use crate::common::asset_names::loan_collateral_asset_name_or_base;
|
2026-05-24 17:56:57 +00:00
|
|
|
use crate::common::nft_assets::nft_asset_name;
|
|
|
|
|
use crate::common::types::Transaction;
|
2026-05-26 06:24:57 +00:00
|
|
|
use crate::decode;
|
2026-05-24 17:56:57 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
async fn restore_if_spendable<F, Fut>(signatures: &[String], spendable: bool, insert: F)
|
|
|
|
|
where
|
|
|
|
|
F: FnOnce() -> Fut,
|
|
|
|
|
Fut: std::future::Future<Output = ()>,
|
|
|
|
|
{
|
|
|
|
|
if !spendable {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match restore_processed_by_signatures(signatures).await {
|
|
|
|
|
Ok(false) | Err(_) => insert().await,
|
|
|
|
|
Ok(true) => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn loan_coin_for_contract(db: &Db, contract_hash: &str) -> Option<String> {
|
|
|
|
|
let contract_key = decode(contract_hash).ok()?;
|
|
|
|
|
let RpcResponse::Binary(bytes) = request_transaction_by_txid(db, contract_key).await;
|
|
|
|
|
|
|
|
|
|
if bytes.is_empty() || bytes[0] != 7 {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LoanContractTransaction::from_bytes(7, &bytes[1..])
|
|
|
|
|
.await
|
|
|
|
|
.ok()
|
|
|
|
|
.map(|contract| contract.unsigned_loan_contract.loan_coin)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_transfer(transaction: &TransferTransaction, db: &Db) {
|
|
|
|
|
let transfer = &transaction.unsigned_transfer;
|
|
|
|
|
let asset = nft_asset_name(&transfer.coin, transfer.nft_series);
|
|
|
|
|
let spendable =
|
|
|
|
|
balance_checkup(db, transfer.value, transfer.txfee, asset, &transfer.sender).await;
|
|
|
|
|
let signature = transaction.signature.clone();
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(&[signature], spendable, || async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
})
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_burn(transaction: &BurnTransaction, db: &Db) {
|
|
|
|
|
let burn = &transaction.unsigned_burn;
|
|
|
|
|
let asset = nft_asset_name(&burn.coin, burn.nft_series);
|
|
|
|
|
let spendable = balance_checkup(db, burn.value, burn.txfee, asset, &burn.address).await;
|
|
|
|
|
let signature = transaction.signature.clone();
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(&[signature], spendable, || async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
})
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_create_token(transaction: &CreateTokenTransaction, db: &Db) {
|
|
|
|
|
let token = &transaction.unsigned_create_token;
|
|
|
|
|
let spendable = balance_checkup(db, 0, token.txfee, BASECOIN.clone(), &token.creator).await;
|
|
|
|
|
let signature = transaction.signature.clone();
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(&[signature], spendable, || async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
})
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_issue_token(transaction: &IssueTokenTransaction, db: &Db) {
|
|
|
|
|
let token = &transaction.unsigned_issue_token;
|
|
|
|
|
let spendable = balance_checkup(db, 0, token.txfee, BASECOIN.clone(), &token.creator).await;
|
|
|
|
|
let signature = transaction.signature.clone();
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(&[signature], spendable, || async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
})
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_create_nft(transaction: &CreateNftTransaction, db: &Db) {
|
|
|
|
|
let nft = &transaction.unsigned_create_nft;
|
|
|
|
|
let spendable = balance_checkup(db, 0, nft.txfee, BASECOIN.clone(), &nft.creator).await;
|
|
|
|
|
let signature = transaction.signature.clone();
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(&[signature], spendable, || async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
})
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_marketing(transaction: &MarketingTransaction, db: &Db) {
|
|
|
|
|
let marketing = &transaction.unsigned_marketing;
|
2026-05-26 06:24:57 +00:00
|
|
|
let spendable = balance_checkup(
|
|
|
|
|
db,
|
|
|
|
|
0,
|
|
|
|
|
marketing.txfee,
|
|
|
|
|
BASECOIN.clone(),
|
|
|
|
|
&marketing.advertiser,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
2026-05-24 17:56:57 +00:00
|
|
|
let signature = transaction.signature.clone();
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(&[signature], spendable, || async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
})
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_swap(transaction: &SwapTransaction, db: &Db) {
|
|
|
|
|
let swap = &transaction.unsigned_swap;
|
|
|
|
|
let asset1 = nft_asset_name(&swap.ticker1, swap.nft_series1);
|
|
|
|
|
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);
|
2026-05-26 06:24:57 +00:00
|
|
|
let sender1_spendable = balance_checkup(db, value1, swap.txfee1, asset1, &swap.sender1).await;
|
|
|
|
|
let sender2_spendable = balance_checkup(db, value2, swap.txfee2, asset2, &swap.sender2).await;
|
|
|
|
|
let signatures = vec![
|
|
|
|
|
transaction.signature1.clone(),
|
|
|
|
|
transaction.signature2.clone(),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(
|
|
|
|
|
&signatures,
|
|
|
|
|
sender1_spendable && sender2_spendable,
|
|
|
|
|
|| async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
},
|
|
|
|
|
)
|
2026-05-24 17:56:57 +00:00
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_loan_creation(transaction: &LoanContractTransaction, db: &Db) {
|
|
|
|
|
let loan = &transaction.unsigned_loan_contract;
|
2026-06-27 16:28:13 +00:00
|
|
|
let Some(collateral) = loan_collateral_asset_name_or_base(&loan.collateral, &BASECOIN) else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
2026-05-26 06:24:57 +00:00
|
|
|
let lender_spendable = balance_checkup(
|
|
|
|
|
db,
|
|
|
|
|
loan.loan_amount,
|
|
|
|
|
loan.txfee,
|
|
|
|
|
loan.loan_coin.clone(),
|
|
|
|
|
&loan.lender,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
2026-06-27 16:28:13 +00:00
|
|
|
let borrower_spendable =
|
|
|
|
|
balance_checkup(db, loan.collateral_amount, 0, collateral, &loan.borrower).await;
|
2026-05-26 06:24:57 +00:00
|
|
|
let signatures = vec![
|
|
|
|
|
transaction.signature1.clone(),
|
|
|
|
|
transaction.signature2.clone(),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(
|
|
|
|
|
&signatures,
|
|
|
|
|
lender_spendable && borrower_spendable,
|
|
|
|
|
|| async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
},
|
|
|
|
|
)
|
2026-05-24 17:56:57 +00:00
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_borrower(transaction: &ContractPaymentTransaction, db: &Db) {
|
|
|
|
|
let payment = &transaction.unsigned_contract_payment;
|
|
|
|
|
let Some(loan_coin) = loan_coin_for_contract(db, &payment.contract_hash).await else {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
let value = payment.payback_amount.saturating_add(payment.tip);
|
|
|
|
|
let spendable = balance_checkup(db, value, payment.txfee, loan_coin, &payment.address).await;
|
|
|
|
|
let signature = transaction.signature.clone();
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(&[signature], spendable, || async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
})
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_collateral(transaction: &CollateralClaimTransaction, db: &Db) {
|
|
|
|
|
let collateral = &transaction.unsigned_collateral_claim;
|
2026-05-26 06:24:57 +00:00
|
|
|
let spendable = balance_checkup(
|
|
|
|
|
db,
|
|
|
|
|
0,
|
|
|
|
|
collateral.txfee,
|
|
|
|
|
BASECOIN.clone(),
|
|
|
|
|
&collateral.address,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
2026-05-24 17:56:57 +00:00
|
|
|
let signature = transaction.signature.clone();
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(&[signature], spendable, || async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
})
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_vanity(transaction: &VanityAddressTransaction, db: &Db) {
|
|
|
|
|
let vanity = &transaction.unsigned_vanity_address;
|
|
|
|
|
let spendable = balance_checkup(db, 0, vanity.txfee, BASECOIN.clone(), &vanity.address).await;
|
|
|
|
|
let signature = transaction.signature.clone();
|
|
|
|
|
|
|
|
|
|
restore_if_spendable(&[signature], spendable, || async {
|
|
|
|
|
let _ = transaction.add_to_memory().await;
|
|
|
|
|
})
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn restore_rolled_back_transaction(transaction: &Transaction, db: &Db) {
|
|
|
|
|
match transaction {
|
|
|
|
|
Transaction::Transfer(tx) => restore_transfer(tx, db).await,
|
|
|
|
|
Transaction::Burn(tx) => restore_burn(tx, db).await,
|
|
|
|
|
Transaction::Token(tx) => restore_create_token(tx, db).await,
|
|
|
|
|
Transaction::IssueToken(tx) => restore_issue_token(tx, db).await,
|
|
|
|
|
Transaction::Nft(tx) => restore_create_nft(tx, db).await,
|
|
|
|
|
Transaction::Marketing(tx) => restore_marketing(tx, db).await,
|
|
|
|
|
Transaction::Swap(tx) => restore_swap(tx, db).await,
|
|
|
|
|
Transaction::Lender(tx) => restore_loan_creation(tx, db).await,
|
|
|
|
|
Transaction::Borrower(tx) => restore_borrower(tx, db).await,
|
|
|
|
|
Transaction::Collateral(tx) => restore_collateral(tx, db).await,
|
|
|
|
|
Transaction::Vanity(tx) => restore_vanity(tx, db).await,
|
|
|
|
|
Transaction::Genesis(_) | Transaction::Rewards(_) => {}
|
|
|
|
|
}
|
|
|
|
|
}
|