Skip to content

Commit

Permalink
Merge pull request #3 from k0k0ne/transfer-history
Browse files Browse the repository at this point in the history
Reproducing examples for the unexpected transfer history bug
  • Loading branch information
zoedberg authored Nov 24, 2024
2 parents 59dec46 + 6353b49 commit 67c6b65
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 2 deletions.
99 changes: 99 additions & 0 deletions tests/transfers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1089,3 +1089,102 @@ fn receive_from_unbroadcasted_transfer_to_blinded() {
Failure::SealNoPubWitness(_, _, _)
));
}

#[test]
fn check_fungible_history() {
initialize();

let mut wlt_1 = get_wallet(&DescriptorType::Wpkh);
let mut wlt_2 = get_wallet(&DescriptorType::Wpkh);

let issue_supply = 600;

let (contract_id, iface_type_name) = wlt_1.issue_nia(issue_supply, wlt_1.close_method(), None);

wlt_1.debug_contracts();
wlt_1.debug_history(contract_id, &iface_type_name, false);

wlt_1.check_history_operation(
&contract_id,
&iface_type_name,
None,
OpDirection::Issued,
issue_supply,
);

let amt = 200;

let (_, tx) = wlt_1.send(
&mut wlt_2,
TransferType::Witness,
contract_id,
&iface_type_name,
amt,
1000,
None,
);
let txid = tx.txid();

wlt_1.debug_history(contract_id, &iface_type_name, false);
wlt_2.debug_history(contract_id, &iface_type_name, false);

wlt_1.check_history_operation(
&contract_id,
&iface_type_name,
Some(&txid),
OpDirection::Sent,
amt,
);

wlt_2.check_history_operation(
&contract_id,
&iface_type_name,
Some(&txid),
OpDirection::Received,
amt,
);
}

#[test]
fn send_to_oneself() {
initialize();

let mut wlt = get_wallet(&DescriptorType::Wpkh);

let issue_supply = 600;

let (contract_id, iface_type_name) = wlt.issue_nia(issue_supply, wlt.close_method(), None);

let amt = 200;

let invoice = wlt.invoice(
contract_id,
&iface_type_name,
amt,
wlt.close_method(),
InvoiceType::Witness,
);

let (consignment, tx) = wlt.transfer(invoice.clone(), None, None, true, None);
wlt.mine_tx(&tx.txid(), false);
wlt.accept_transfer(consignment, None);
wlt.sync();

wlt.debug_history(contract_id, &iface_type_name, false);
let history = wlt.history(contract_id, &iface_type_name);
// only issue operation is found, because self-transfers should not appear in history
assert_eq!(history.len(), 1);

wlt.debug_logs(
contract_id,
&iface_type_name.clone(),
AllocationFilter::WalletAll,
);
wlt.check_allocations(
contract_id,
&iface_type_name,
AssetSchema::Nia,
vec![amt, issue_supply - amt],
true,
);
}
85 changes: 85 additions & 0 deletions tests/utils/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,19 @@ impl TestWallet {
.collect()
}

pub fn history(&self, contract_id: ContractId, iface_type_name: &TypeName) -> Vec<ContractOp> {
self.wallet
.history(contract_id, iface_type_name.clone())
.unwrap()
}

pub fn debug_contracts(&self) {
println!("Contracts:");
for info in self.wallet.stock().contracts().unwrap() {
println!("{}", info.to_string().replace("\n", "\t"));
}
}

pub fn debug_logs(
&self,
contract_id: ContractId,
Expand Down Expand Up @@ -1098,6 +1111,57 @@ impl TestWallet {
println!("\nWallet total balance: {} ṩ", bp_runtime.balance());
}

pub fn debug_history(
&self,
contract_id: ContractId,
iface_type_name: &TypeName,
details: bool,
) {
let mut history = self.history(contract_id, iface_type_name);
history.sort_by_key(|op| op.witness.map(|w| w.ord).unwrap_or(WitnessOrd::Archived));
if details {
println!("Operation\tValue \tState\t{:78}\tWitness", "Seal");
} else {
println!("Operation\tValue \t{:78}\tWitness", "Seal");
}
for ContractOp {
direction,
ty,
opids,
state,
to,
witness,
} in history
{
print!("{:9}\t", direction.to_string());
if let AllocatedState::Amount(amount) = state {
print!("{: >9}", amount.value());
} else {
print!("{state:>9}");
}
if details {
print!("\t{ty}");
}
println!(
"\t{}\t{}",
to.first().expect("at least one receiver is always present"),
witness
.map(|info| format!("{} ({})", info.id, info.ord))
.unwrap_or_else(|| s!("~"))
);
if details {
println!(
"\topid={}",
opids
.iter()
.map(OpId::to_string)
.collect::<Vec<_>>()
.join("\n\topid=")
)
}
}
}

#[allow(clippy::too_many_arguments)]
pub fn send(
&mut self,
Expand Down Expand Up @@ -1188,6 +1252,27 @@ impl TestWallet {
}
}

pub fn check_history_operation(
&self,
contract_id: &ContractId,
iface_type_name: &TypeName,
txid: Option<&Txid>,
direction: OpDirection,
amount: u64,
) {
let operation = self
.history(*contract_id, iface_type_name)
.into_iter()
.find(|co| {
co.direction == direction
&& co
.witness
.map_or(true, |w| Some(w.id.as_reduced_unsafe()) == txid)
})
.unwrap();
assert!(matches!(operation.state, AllocatedState::Amount(amt) if amt.value() == amount));
}

fn _construct_psbt_offchain(
&mut self,
input_outpoints: Vec<(Outpoint, u64, Terminal)>,
Expand Down
4 changes: 2 additions & 2 deletions tests/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ pub use psbt::{
pub use psrgbt::{RgbExt, RgbInExt, RgbPsbt, TxParams};
pub use rand::RngCore;
pub use rgb::{
interface::AssignmentsFilter,
interface::{AllocatedState, AssignmentsFilter, ContractOp, OpDirection},
invoice::Pay2Vout,
persistence::{MemContract, MemContractState, Stock},
resolvers::AnyResolver,
stl::ContractTerms,
validation::{Failure, ResolveWitness, Scripts, Validity, WitnessResolverError},
vm::{WitnessOrd, WitnessPos, XWitnessTx},
BlindingFactor, DescriptorRgb, GenesisSeal, GraphSeal, Identity, RgbDescr, RgbKeychain,
BlindingFactor, DescriptorRgb, GenesisSeal, GraphSeal, Identity, OpId, RgbDescr, RgbKeychain,
RgbWallet, TapretKey, TransferParams, Transition, WalletProvider, XOutpoint, XWitnessId,
};
pub use rgbstd::{
Expand Down

0 comments on commit 67c6b65

Please sign in to comment.