Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 40 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ impl SimulationResult {
self.total_payment_obligations
}

pub fn precentage_of_payment_obligations_missed(&self) -> f64 {
pub fn percentage_of_payment_obligations_missed(&self) -> f64 {
let total_payment_obligations = self.total_payment_obligations();
self.missed_payment_obligations
.iter()
Expand Down Expand Up @@ -786,22 +786,20 @@ mod tests {
let result = sim.run();
sim.assert_invariants();

println!("{}", sim);
result.save_tx_graph("graph.svg");
// Lets check the simulation state after the run
// Specifically how many payment obligations we're missed
// And how many were created in a cospend
println!(
"Total payment obligations: {}",
result.total_payment_obligations()
// Assert simulation completed successfully
assert!(
result.total_payment_obligations() > 0,
"Simulation should create payment obligations"
);
println!(
"Missed payment obligations: {:?}",
result.missed_payment_obligations
assert!(
result.percentage_of_payment_obligations_missed() < 1.0,
"Not all obligations should be missed"
);
println!(
"Missed payment obligations percentage: {:?}",
result.precentage_of_payment_obligations_missed()

assert_eq!(
result.percentage_of_payment_obligations_missed(),
0.5384615384615384,
"With seed 42, missed percentage should be deterministic"
);
}

Expand Down Expand Up @@ -983,8 +981,33 @@ mod tests {

assert!(bob.with(&sim).info().received_transactions.contains(&spend));

// TODO mine another block, check wallet utxos, et
// Mine another block to confirm the transaction
let miner_addr = alice.with_mut(&mut sim).new_address();
let block_bx = BroadcastSetHandleMut {
id: BroadcastSetId(sim.broadcast_set_data.len() - 1),
sim: &mut sim,
};

let _block = block_bx
.construct_block_template(Weight::MAX_BLOCK)
.mine(miner_addr, &mut sim);

sim.assert_invariants();

println!("{:?}", sim);
// Verify transaction is now confirmed and UTXOs are updated
assert!(alice
.with(&sim)
.info()
.confirmed_utxos
.contains(&spend.with(&sim).outpoints().nth(1).unwrap()));
assert!(bob
.with(&sim)
.info()
.confirmed_utxos
.contains(&spend.with(&sim).outpoints().nth(0).unwrap()));

// Verify the spend transaction is no longer unconfirmed
assert!(alice.with(&sim).info().unconfirmed_txos.is_empty());
assert!(bob.with(&sim).info().unconfirmed_txos.is_empty());
}
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ fn main() {
);
println!(
"Missed payment obligations percentage: {:?}",
result.precentage_of_payment_obligations_missed()
result.percentage_of_payment_obligations_missed()
);
}
21 changes: 20 additions & 1 deletion src/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::wallet::{AddressHandle, AddressId, WalletHandle, WalletHandleMut, WalletId};
use crate::Simulation;
use bitcoin::consensus::Decodable;
use bitcoin::hashes::Hash;
use bitcoin::transaction::{predict_weight, InputWeightPrediction};
use bitcoin::{Amount, Weight};
use bitcoin::{FeeRate, ScriptBuf, WitnessProgram};
Expand Down Expand Up @@ -29,6 +30,16 @@ impl From<TxId> for bitcoin::Txid {
}
}

impl From<bitcoin::Txid> for TxId {
fn from(bitcoin_txid: bitcoin::Txid) -> Self {
let bytes = bitcoin_txid.as_byte_array();
// Extract first 8 bytes and convert to usize (little endian)
let mut txid_bytes = [0u8; 8];
txid_bytes.copy_from_slice(&bytes[..8]);
TxId(u64::from_le_bytes(txid_bytes) as usize)
}
}

// TODO rename to OutputId?
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub(crate) struct Outpoint {
Expand Down Expand Up @@ -296,11 +307,19 @@ impl TxInfo {

#[cfg(test)]
mod tests {
use bitcoin::hashes::Hash;

use super::*;

#[test]
fn test_txid_encoding() {
let txid = TxId(1);
let txid_from_bytes = bitcoin::Txid::from(txid);
let bitcoin_txid = bitcoin::Txid::from(txid);

assert_eq!(bitcoin_txid.as_byte_array()[0], 1);
assert_eq!(bitcoin_txid.to_byte_array()[1..], [0u8; 31]);

let converted_back = TxId::from(bitcoin_txid);
assert_eq!(converted_back, txid);
}
}
53 changes: 52 additions & 1 deletion src/tx_contruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,59 @@ mod tests {
let txdata = sent_ready
.have_enough_ready_to_sign()
.expect("should have enough ready to sign");
// TODO: assert the inputs and outputs are correct

// Verify input composition (2 from template + 2 from others)
assert_eq!(txdata.inputs.len(), 4);

// Collect all TxIds from inputs
let input_txids: Vec<usize> = txdata
.inputs
.iter()
.map(|input| input.outpoint.txid.0)
.collect();

// Should have template inputs (TxId(0) and TxId(1))
assert!(
input_txids.contains(&0),
"Should contain template input TxId(0)"
);
assert!(
input_txids.contains(&1),
"Should contain template input TxId(1)"
);

// Should have other participant inputs (TxId(100) and TxId(101))
assert!(
input_txids.contains(&100),
"Should contain other participant input TxId(100)"
);
assert!(
input_txids.contains(&101),
"Should contain other participant input TxId(101)"
);

// Verify output composition (1 from template + 2 from others)
assert_eq!(txdata.outputs.len(), 3);

// Should have 1 output with 1000 sats (from template) and 2 outputs with 2000 sats (from others)
let output_1000_count = txdata
.outputs
.iter()
.filter(|output| output.amount == Amount::from_sat(1000))
.count();
let output_2000_count = txdata
.outputs
.iter()
.filter(|output| output.amount == Amount::from_sat(2000))
.count();

assert_eq!(
output_1000_count, 1,
"Should have exactly 1 output with 1000 sats from template"
);
assert_eq!(
output_2000_count, 2,
"Should have exactly 2 outputs with 2000 sats from other participants"
);
}
}