finish verify_bond_tx_hex() unit tests

This commit is contained in:
f321x
2024-07-10 14:29:35 +00:00
parent b56380d448
commit 5540c578b5
2 changed files with 41 additions and 164 deletions

View File

@ -168,17 +168,54 @@ mod tests {
#[tokio::test]
async fn test_transaction_without_signature() {
panic!("Not implemented");
let test_wallet = new_test_wallet("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32").await;
let bond_without_signature = "02000000010127a9d96655011fca55dc2667f30b98655e46da98d0f84df676b53d7fb380140000000000fdffffff02998d0000000000002251207dd0d1650cdc22537709e35620f3b5cc3249b305bda1209ba4e5e01bc3ad2d8c50c3000000000000225120a12e5d145a4a3ab43f6cc1188435e74f253eace72bd986f1aaf780fd0c6532364f860000";
let requirements = BondRequirements {
min_input_sum_sat: 51000,
locking_amount_sat: 50000,
bond_address: "tb1p5yh969z6fgatg0mvcyvggd08fujnat8890vcdud277q06rr9xgmqwfdkcx"
.to_string(),
};
let result = test_wallet
.validate_bond_tx_hex(&bond_without_signature, &requirements)
.await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_transaction_with_invalid_signature() {
panic!("Not implemented");
let test_wallet = new_test_wallet("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32").await;
// assembled bond tx but with the signature of a different bond = invalid
let bond_with_invalid_signature = "020000000001010127a9d96655011fca55dc2667f30b98655e46da98d0f84df676b53d7fb3801400000000001900000002aa900000000000002251207dd0d1650cdc22537709e35620f3b5cc3249b305bda1209ba4e5e01bc3ad2d8c50c3000000000000225120a12e5d145a4a3ab43f6cc1188435e74f253eace72bd986f1aaf780fd0c65323601401fddcc681a1d0324c8fdeabbc08a3b06c26741872363c0ddfc82f15b6abe43d37815bcdc2ce1fb2f70cac426f7fb269d322ac6a621886208d0c625335bba670800000000";
let requirements = BondRequirements {
min_input_sum_sat: 51000,
locking_amount_sat: 50000,
bond_address: "tb1p5yh969z6fgatg0mvcyvggd08fujnat8890vcdud277q06rr9xgmqwfdkcx"
.to_string(),
};
let result = test_wallet
.validate_bond_tx_hex(&bond_with_invalid_signature, &requirements)
.await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_transaction_with_spent_input() {
panic!("Not implemented");
async fn test_bond_with_spent_input() {
let test_wallet = new_test_wallet("tprv8ZgxMBicQKsPdHuCSjhQuSZP1h6ZTeiRqREYS5guGPdtL7D1uNLpnJmb2oJep99Esq1NbNZKVJBNnD2ZhuXSK7G5eFmmcx73gsoa65e2U32").await;
let bond_with_spent_input = "02000000000101f7d992795b0b43227ea83e296a7c2a91771ede3ef54f1eb5664393c79b9399080100000000fdffffff0250c3000000000000225120a12e5d145a4a3ab43f6cc1188435e74f253eace72bd986f1aaf780fd0c653236abc6010000000000225120b83c64b440203fb74a0c672cd829f387b957129835dd3b5c4e33fc71a146b3ae0140afdafbae5b76217f469790b211d7fbda427e5b4379c4603e9ae08c9ef5aaae30bfecfc16e5f636c737bea8e0e27974854d1cd0d094ed737aadfc679a974074574f860000";
let requirements = BondRequirements {
min_input_sum_sat: 51000,
locking_amount_sat: 50000,
bond_address: "tb1p5yh969z6fgatg0mvcyvggd08fujnat8890vcdud277q06rr9xgmqwfdkcx"
.to_string(),
};
let result = test_wallet
.validate_bond_tx_hex(&bond_with_spent_input, &requirements)
.await;
assert!(result.is_err());
}
#[tokio::test]

View File

@ -1,160 +0,0 @@
////////////////////////////////////////////////////////////////////////////
// copied from bdk as verify_tx does not compile with async esplora backend.
// needs to be updated in case we want to use async esplora
////////////////////////////////////////////////////////////////////////////
//! Verify transactions against the consensus rules
use std::collections::HashMap;
use std::fmt;
use bdk::bitcoin::consensus::serialize;
use bdk::bitcoin::{OutPoint, Transaction, Txid};
use bdk::blockchain::GetTx;
use bdk::database::Database;
// use bdk::error::Error;
/// Verify a transaction against the consensus rules
///
/// This function uses [`bitcoinconsensus`] to verify transactions by fetching the required data
/// either from the [`Database`] or using the [`Blockchain`].
///
/// Depending on the [capabilities](crate::blockchain::Blockchain::get_capabilities) of the
/// [`Blockchain`] backend, the method could fail when called with old "historical" transactions or
/// with unconfirmed transactions that have been evicted from the backend's memory.
///
/// [`Blockchain`]: crate::blockchain::Blockchain
pub fn verify_tx_mod<D: Database, B: GetTx>(
tx: &Transaction,
database: &D,
blockchain: &B,
) -> Result<(), VerifyError> {
// log::debug!("Verifying {}", tx.txid());
let serialized_tx = serialize(tx);
let mut tx_cache = HashMap::<_, Transaction>::new();
for (index, input) in tx.input.iter().enumerate() {
let prev_tx = if let Some(prev_tx) = tx_cache.get(&input.previous_output.txid) {
prev_tx.clone()
} else if let Some(prev_tx) = database.get_raw_tx(&input.previous_output.txid)? {
prev_tx
} else if let Some(prev_tx) = blockchain.get_tx(&input.previous_output.txid)? {
prev_tx
} else {
return Err(VerifyError::MissingInputTx(input.previous_output.txid));
};
let spent_output = prev_tx
.output
.get(input.previous_output.vout as usize)
.ok_or(VerifyError::InvalidInput(input.previous_output))?;
let res = bitcoinconsensus::verify(
&spent_output.script_pubkey.to_bytes(),
spent_output.value,
&serialized_tx,
index,
);
// Since we have a local cache we might as well cache stuff from the db, as it will very
// likely decrease latency compared to reading from disk or performing an SQL query.
tx_cache.insert(prev_tx.txid(), prev_tx);
}
Ok(())
}
/// Error during validation of a tx agains the consensus rules
#[derive(Debug)]
pub enum VerifyError {
/// The transaction being spent is not available in the database or the blockchain client
MissingInputTx(Txid),
/// The transaction being spent doesn't have the requested output
InvalidInput(OutPoint),
/// Consensus error
Consensus(bitcoinconsensus::Error),
/// Generic error
///
/// It has to be wrapped in a `Box` since `Error` has a variant that contains this enum
Global(Box<Error>),
}
impl fmt::Display for VerifyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingInputTx(txid) => write!(f, "The transaction being spent is not available in the database or the blockchain client: {}", txid),
Self::InvalidInput(outpoint) => write!(f, "The transaction being spent doesn't have the requested output: {}", outpoint),
Self::Consensus(err) => write!(f, "Consensus error: {:?}", err),
Self::Global(err) => write!(f, "Generic error: {}", err),
}
}
}
impl std::error::Error for VerifyError {}
impl From<Error> for VerifyError {
fn from(other: Error) -> Self {
VerifyError::Global(Box::new(other))
}
}
// impl_error!(bitcoinconsensus::Error, Consensus, VerifyError);
// #[cfg(test)]
// mod test {
// use super::*;
// use crate::database::{BatchOperations, MemoryDatabase};
// use assert_matches::assert_matches;
// use bitcoin::consensus::encode::deserialize;
// use bitcoin::hashes::hex::FromHex;
// use bitcoin::{Transaction, Txid};
// struct DummyBlockchain;
// impl GetTx for DummyBlockchain {
// fn get_tx(&self, _txid: &Txid) -> Result<Option<Transaction>, Error> {
// Ok(None)
// }
// }
// #[test]
// fn test_verify_fail_unsigned_tx() {
// // https://blockstream.info/tx/95da344585fcf2e5f7d6cbf2c3df2dcce84f9196f7a7bb901a43275cd6eb7c3f
// let prev_tx: Transaction = deserialize(&Vec::<u8>::from_hex("020000000101192dea5e66d444380e106f8e53acb171703f00d43fb6b3ae88ca5644bdb7e1000000006b48304502210098328d026ce138411f957966c1cf7f7597ccbb170f5d5655ee3e9f47b18f6999022017c3526fc9147830e1340e04934476a3d1521af5b4de4e98baf49ec4c072079e01210276f847f77ec8dd66d78affd3c318a0ed26d89dab33fa143333c207402fcec352feffffff023d0ac203000000001976a9144bfbaf6afb76cc5771bc6404810d1cc041a6933988aca4b956050000000017a91494d5543c74a3ee98e0cf8e8caef5dc813a0f34b48768cb0700").unwrap()).unwrap();
// // https://blockstream.info/tx/aca326a724eda9a461c10a876534ecd5ae7b27f10f26c3862fb996f80ea2d45d
// let signed_tx: Transaction = deserialize(&Vec::<u8>::from_hex("02000000013f7cebd65c27431a90bba7f796914fe8cc2ddfc3f2cbd6f7e5f2fc854534da95000000006b483045022100de1ac3bcdfb0332207c4a91f3832bd2c2915840165f876ab47c5f8996b971c3602201c6c053d750fadde599e6f5c4e1963df0f01fc0d97815e8157e3d59fe09ca30d012103699b464d1d8bc9e47d4fb1cdaa89a1c5783d68363c4dbc4b524ed3d857148617feffffff02836d3c01000000001976a914fc25d6d5c94003bf5b0c7b640a248e2c637fcfb088ac7ada8202000000001976a914fbed3d9b11183209a57999d54d59f67c019e756c88ac6acb0700").unwrap()).unwrap();
// let mut database = MemoryDatabase::new();
// let blockchain = DummyBlockchain;
// let mut unsigned_tx = signed_tx.clone();
// for input in &mut unsigned_tx.input {
// input.script_sig = Default::default();
// input.witness = Default::default();
// }
// let result = verify_tx_mod(&signed_tx, &database, &blockchain);
// assert_matches!(result, Err(VerifyError::MissingInputTx(txid)) if txid == prev_tx.txid(),
// "Error should be a `MissingInputTx` error"
// );
// // insert the prev_tx
// database.set_raw_tx(&prev_tx).unwrap();
// let result = verify_tx_mod(&unsigned_tx, &database, &blockchain);
// assert_matches!(
// result,
// Err(VerifyError::Consensus(_)),
// "Error should be a `Consensus` error"
// );
// let result = verify_tx_mod(&signed_tx, &database, &blockchain);
// assert!(
// result.is_ok(),
// "Should work since the TX is correctly signed"
// );
// }
// }