106 lines
4.4 KiB
Rust
106 lines
4.4 KiB
Rust
|
|
use crate::blocks::loan_payment::ContractPaymentTransaction;
|
||
|
|
use crate::blocks::loans::LoanContractTransaction;
|
||
|
|
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::add_payments_db::remove_payment;
|
||
|
|
use crate::records::record_chain::nft_provenance::remove_nft_history_entry;
|
||
|
|
use crate::rpc::commands::transaction_by_txid::request_transaction_by_txid;
|
||
|
|
use crate::rpc::responses::RpcResponse;
|
||
|
|
use crate::sled::Db;
|
||
|
|
|
||
|
|
pub async fn undo_borrower_transaction(
|
||
|
|
transaction: ContractPaymentTransaction,
|
||
|
|
mining_receiver: &str,
|
||
|
|
db: &Db,
|
||
|
|
) -> Result<(), String> {
|
||
|
|
// restore balances and database state for a contract payment
|
||
|
|
// that is being removed during orphan rollback
|
||
|
|
let operand_subtraction = "subtraction";
|
||
|
|
let operand_addition = "addition";
|
||
|
|
let (
|
||
|
|
_network_name,
|
||
|
|
_padded_base_coin,
|
||
|
|
type_str,
|
||
|
|
_torrentpath,
|
||
|
|
_wallet_path,
|
||
|
|
_blockpath,
|
||
|
|
_db_path,
|
||
|
|
_balance_path,
|
||
|
|
_log_path,
|
||
|
|
) = block_extension_and_paths();
|
||
|
|
|
||
|
|
// reload the original loan contract so the rollback uses the
|
||
|
|
// same lender and asset information the payment was based on
|
||
|
|
let contract_hash = decode(&transaction.unsigned_contract_payment.contract_hash)
|
||
|
|
.map_err(|e| format!("Error decoding contract hash: {e}"))?;
|
||
|
|
let contract = request_transaction_by_txid(db, contract_hash.clone()).await;
|
||
|
|
|
||
|
|
let loan_txtype = 7;
|
||
|
|
let loan_tx = match contract {
|
||
|
|
RpcResponse::Binary(contract_bytes) => {
|
||
|
|
if contract_bytes.is_empty() {
|
||
|
|
return Err("Invalid loan contract: empty transaction bytes".to_string());
|
||
|
|
}
|
||
|
|
if contract_bytes[0] != loan_txtype {
|
||
|
|
return Err(
|
||
|
|
"Invalid loan contract: referenced transaction is not a loan contract"
|
||
|
|
.to_string(),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
LoanContractTransaction::from_bytes(loan_txtype, &contract_bytes[1..])
|
||
|
|
.await
|
||
|
|
.map_err(|e| e.to_string())?
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
let lender = loan_tx.unsigned_loan_contract.lender;
|
||
|
|
let loan_coin = loan_tx.unsigned_loan_contract.loan_coin;
|
||
|
|
let borrower = &transaction.unsigned_contract_payment.address;
|
||
|
|
let payback_amount = transaction.unsigned_contract_payment.payback_amount;
|
||
|
|
let tip = transaction.unsigned_contract_payment.tip;
|
||
|
|
let txfee = transaction.unsigned_contract_payment.txfee;
|
||
|
|
|
||
|
|
// reverse the fee, tip, and repayment movements that were
|
||
|
|
// applied when the borrower payment was originally saved
|
||
|
|
let _ =
|
||
|
|
balance_sheet_operation_with_db(db, mining_receiver, txfee, &type_str, operand_subtraction);
|
||
|
|
let _ = balance_sheet_operation_with_db(db, borrower, txfee, &type_str, operand_addition);
|
||
|
|
let _ =
|
||
|
|
balance_sheet_operation_with_db(db, mining_receiver, tip, &loan_coin, operand_subtraction);
|
||
|
|
let _ = balance_sheet_operation_with_db(db, borrower, tip, &loan_coin, operand_addition);
|
||
|
|
let _ = balance_sheet_operation_with_db(
|
||
|
|
db,
|
||
|
|
&lender,
|
||
|
|
payback_amount,
|
||
|
|
&loan_coin,
|
||
|
|
operand_subtraction,
|
||
|
|
);
|
||
|
|
let _ =
|
||
|
|
balance_sheet_operation_with_db(db, borrower, payback_amount, &loan_coin, operand_addition);
|
||
|
|
|
||
|
|
// Remove the payment transaction lookup from the txid tree.
|
||
|
|
let txid_tree = db
|
||
|
|
.open_tree("txid")
|
||
|
|
.map_err(|e| format!("Failed to open txid tree: {e}"))?;
|
||
|
|
let tx_hash = transaction.unsigned_contract_payment.hash().await;
|
||
|
|
txid_tree
|
||
|
|
.remove(decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?)
|
||
|
|
.map_err(|e| format!("Failed to remove borrower txid: {e}"))?;
|
||
|
|
|
||
|
|
// Loan payments involving NFTs also add a provenance entry for the loan
|
||
|
|
// asset, so remove it if this loan coin is tracked as an NFT.
|
||
|
|
let nft_tree = db
|
||
|
|
.open_tree("nfts")
|
||
|
|
.map_err(|e| format!("Failed to open nfts tree: {e}"))?;
|
||
|
|
let tx_hash_bytes = decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?;
|
||
|
|
if nft_tree.contains_key(loan_coin.as_bytes()).unwrap_or(false) {
|
||
|
|
let _ = remove_nft_history_entry(db, &loan_coin, &tx_hash_bytes);
|
||
|
|
}
|
||
|
|
|
||
|
|
// The aggregate payment record is reduced by the payment being undone.
|
||
|
|
let _ = remove_payment(db, contract_hash, payback_amount);
|
||
|
|
|
||
|
|
Ok(())
|
||
|
|
}
|