use crate::blocks::collateral::CollateralClaimTransaction; 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::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_collateral_transaction( transaction: CollateralClaimTransaction, mining_receiver: &str, db: &Db, ) -> Result<(), String> { // restore balances and contract state for a collateral // claim 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 collateral // asset and amount can be restored correctly let contract_hash = decode(&transaction.unsigned_collateral_claim.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 collateral = loan_tx.unsigned_loan_contract.collateral; let collateral_amount = loan_tx.unsigned_loan_contract.collateral_amount; let collateral_holding = format!( "collateral_{}", transaction.unsigned_collateral_claim.contract_hash ); let claimer = &transaction.unsigned_collateral_claim.address; let txfee = transaction.unsigned_collateral_claim.txfee; // reverse the fee and move the collateral back into the // contract holding wallet until the claim exists again let _ = balance_sheet_operation_with_db(db, mining_receiver, txfee, &type_str, operand_subtraction); let _ = balance_sheet_operation_with_db(db, claimer, txfee, &type_str, operand_addition); let _ = balance_sheet_operation_with_db( db, claimer, collateral_amount, &collateral, operand_subtraction, ); let _ = balance_sheet_operation_with_db( db, &collateral_holding, collateral_amount, &collateral, operand_addition, ); // Remove the collateral-claim transaction lookup from the txid tree. let txid_tree = db.open_tree("txid").unwrap(); let tx_hash = transaction.unsigned_collateral_claim.hash().await; txid_tree .remove(decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?) .unwrap(); // NFT collateral claims write provenance for the collateral asset. let nft_tree = db.open_tree("nfts").unwrap(); let tx_hash_bytes = decode(&tx_hash).map_err(|e| format!("Error decoding txid: {e}"))?; if nft_tree .contains_key(collateral.as_bytes()) .unwrap_or(false) { let _ = remove_nft_history_entry(db, &collateral, &tx_hash_bytes); } // Mark the loan contract active again because the collateral claim no // longer exists after rollback. let loan_tree = db.open_tree("loan").unwrap(); loan_tree.insert(contract_hash, "true".as_bytes()).unwrap(); Ok(()) }