use tracing::info;
use crate::primitives::asset::Asset;
use crate::primitives::druid::DruidExpectation;
use crate::primitives::transaction::Transaction;
use crate::utils::transaction_utils::construct_tx_ins_address;
use std::collections::BTreeSet;
use std::iter::Extend;
pub fn druid_expectations_are_met<'a>(
druid: &str,
transactions: impl Iterator<Item = &'a Transaction>,
) -> bool {
let mut expects = BTreeSet::new();
let mut tx_source = BTreeSet::new();
for tx in transactions {
info!("");
if let Some(druid_info) = &tx.druid_info {
let ins = construct_tx_ins_address(&tx.inputs);
if druid_info.druid == druid {
info!("DRUIDs match");
expects.extend(druid_info.expectations.iter());
info!("Expectations: {:?}", expects);
for out in &tx.outputs {
if let Some(pk) = &out.script_public_key {
tx_source.insert((ins.clone(), pk, &out.value));
}
}
info!("Tx Source: {:?}", tx_source);
}
}
info!("");
}
expects.iter().all(|e| expectation_met(e, &tx_source))
}
fn expectation_met(e: &DruidExpectation, tx_source: &BTreeSet<(String, &String, &Asset)>) -> bool {
tx_source.get(&(e.from.clone(), &e.to, &e.asset)).is_some()
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use std::vec;
use super::*;
use crate::crypto::sign_ed25519::{self as sign};
use crate::primitives::asset::{Asset, ItemAsset, TokenAmount};
use crate::primitives::druid::{DdeValues, DruidExpectation};
use crate::primitives::transaction::*;
use crate::utils::transaction_utils::*;
fn create_dde_txs() -> Vec<Transaction> {
let druid = "VALUE".to_owned();
let tx_input = construct_payment_tx_ins(vec![]);
let from_addr = construct_tx_ins_address(&tx_input);
let (pk, sk) = sign::gen_keypair();
let prev_out = OutPoint::new("t_hash".to_string(), 0);
let mut key_material = BTreeMap::new();
key_material.insert(prev_out, (pk, sk));
let amount = TokenAmount(10);
let alice_addr = "3333".to_owned();
let alice_asset = Asset::Token(amount);
let bob_asset = Asset::Item(ItemAsset {
metadata: Some("453094573049875".to_string()),
amount: 1,
genesis_hash: None,
});
let bob_addr = "22222".to_owned();
let token_tx_out = TxOut {
value: alice_asset.clone(),
script_public_key: Some(bob_addr.clone()),
..Default::default()
};
let data_tx_out = TxOut {
value: bob_asset.clone(),
script_public_key: Some(alice_addr.clone()),
..Default::default()
};
let expects = vec![
DruidExpectation {
from: from_addr.clone(),
to: bob_addr,
asset: alice_asset,
},
DruidExpectation {
from: from_addr,
to: alice_addr,
asset: bob_asset,
},
];
let alice_druid_info = DdeValues {
druid: druid.clone(),
participants: 2,
expectations: expects.clone(),
genesis_hash: None,
};
let alice_tx = construct_dde_tx(
alice_druid_info,
tx_input.clone(),
vec![token_tx_out],
None,
&key_material,
);
let bob_druid_info = DdeValues {
druid: druid.clone(),
participants: 2,
expectations: expects.clone(),
genesis_hash: None,
};
let bob_tx = construct_dde_tx(
bob_druid_info,
tx_input,
vec![data_tx_out],
None,
&key_material,
);
vec![alice_tx, bob_tx]
}
fn create_rb_payment_txs() -> (Transaction, Transaction) {
let amount = TokenAmount(33);
let payment = TokenAmount(11);
let druid = "VALUE".to_owned();
let tx_input = construct_payment_tx_ins(vec![]);
let from_addr = construct_tx_ins_address(&tx_input);
let alice_addr = "1111".to_owned();
let bob_addr = "00000".to_owned();
let sender_address_excess = "11112".to_owned();
let mut key_material = BTreeMap::new();
let send_tx = {
let tx_ins = {
construct_payment_tx_ins(vec![])
};
let excess_tx_out =
TxOut::new_token_amount(sender_address_excess, amount - payment, None);
let (pk, sk) = sign::gen_keypair();
let prev_out = OutPoint::new("t_hash".to_string(), 0);
key_material.insert(prev_out, (pk, sk));
let expectation = DruidExpectation {
from: from_addr.clone(),
to: alice_addr.clone(),
asset: Asset::item(1, Some("genesis_hash".to_owned()), None),
};
let druid_info = DdeValues {
druid: druid.clone(),
participants: 2,
expectations: vec![expectation.clone()],
genesis_hash: None,
};
let mut tx = construct_rb_payments_send_tx(
tx_ins,
Vec::new(),
None,
ReceiverInfo {
address: bob_addr.clone(),
asset: Asset::Token(payment),
},
0,
druid_info,
&key_material,
);
tx.outputs.push(excess_tx_out);
tx
};
let recv_tx = {
let tx_ins = {
let tx_ins_constructor = vec![];
construct_payment_tx_ins(tx_ins_constructor)
};
let expectation = DruidExpectation {
from: from_addr,
to: bob_addr,
asset: Asset::Token(payment),
};
let druid_info = DdeValues {
druid: druid.clone(),
participants: 2,
expectations: vec![expectation.clone()],
genesis_hash: Some("genesis_hash".to_owned()),
};
construct_rb_receive_payment_tx(
tx_ins,
Vec::new(),
None,
alice_addr,
0,
druid_info,
&key_material,
)
};
(send_tx, recv_tx)
}
#[test]
fn should_pass_matching_dde_tx_valid() {
let txs = create_dde_txs();
assert!(druid_expectations_are_met("VALUE", txs.iter()));
}
#[test]
fn should_fail_dde_tx_value_expect_mismatch() {
let mut txs = create_dde_txs();
let mut change_tx = txs.pop().unwrap();
let orig_tx = txs[0].clone();
let druid_info = change_tx.druid_info.clone();
let mut expects = druid_info.unwrap().expectations;
expects[0].to = "60764505679457".to_string();
let nm_druid_info = DdeValues {
druid: "VALUE".to_owned(),
participants: 2,
expectations: expects,
genesis_hash: None,
};
change_tx.druid_info = Some(nm_druid_info);
assert!(!druid_expectations_are_met(
"VALUE",
vec![orig_tx, change_tx].iter()
));
}
#[test]
fn should_pass_matching_rb_payment_valid() {
let (send_tx, recv_tx) = create_rb_payment_txs();
assert!(druid_expectations_are_met(
"VALUE",
vec![send_tx, recv_tx].iter()
));
}
#[test]
fn should_fail_rb_payment_druid_mismatch() {
let (send_tx, mut recv_tx) = create_rb_payment_txs();
let mut druid_info = recv_tx.druid_info.unwrap();
druid_info.druid = "Not_VAlue".to_owned();
recv_tx.druid_info = Some(druid_info);
assert!(!druid_expectations_are_met(
"VALUE",
vec![send_tx, recv_tx].iter()
));
}
#[test]
fn should_fail_rb_payment_addr_mismatch() {
let (send_tx, mut recv_tx) = create_rb_payment_txs();
recv_tx.outputs[0].script_public_key = Some("11145".to_string());
assert!(!druid_expectations_are_met(
"VALUE",
vec![send_tx, recv_tx].iter()
));
}
#[test]
fn should_fail_rb_payment_value_expect_mismatch() {
let (mut send_tx, recv_tx) = create_rb_payment_txs();
send_tx.outputs[0].value = Asset::token_u64(10);
assert!(!druid_expectations_are_met(
"VALUE",
vec![send_tx, recv_tx].iter()
));
}
#[test]
fn should_fail_rb_payment_drs_expect_mismatch() {
let (send_tx, mut recv_tx) = create_rb_payment_txs();
recv_tx.outputs[0].value = Asset::item(1, Some("invalid_genesis_hash".to_string()), None);
assert!(!druid_expectations_are_met(
"VALUE",
vec![send_tx, recv_tx].iter()
));
}
}