Contractless/src/orphans/undo_transactions/undo_collateral.rs

109 lines
4.0 KiB
Rust

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(())
}