use crate::database::entities::transfer_transport_endpoint;
use crate::wallet::MAX_TRANSPORT_ENDPOINTS;
use sea_orm::EntityTrait;
use serial_test::parallel;
use super::*;
#[test]
#[parallel]
fn success() {
initialize();
let amount = 69;
let expiration = 60;
let (wallet, online) = get_funded_wallet!();
let bak_info_before = wallet.database.get_backup_info().unwrap().unwrap();
let now_timestamp = now().unix_timestamp();
let receive_data = test_blind_receive(&wallet);
let bak_info_after = wallet.database.get_backup_info().unwrap().unwrap();
assert!(bak_info_after.last_operation_timestamp > bak_info_before.last_operation_timestamp);
assert!(receive_data.expiration_timestamp.is_some());
let timestamp = now_timestamp + DURATION_RCV_TRANSFER as i64;
assert!(receive_data.expiration_timestamp.unwrap() - timestamp <= 1);
let transfer = get_test_transfer_recipient(&wallet, &receive_data.recipient_id);
let (_, batch_transfer) = get_test_transfer_related(&wallet, &transfer);
assert_eq!(batch_transfer.min_confirmations, MIN_CONFIRMATIONS);
let now_timestamp = now().unix_timestamp();
let receive_data = wallet
.blind_receive(
None,
None,
Some(expiration),
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
)
.unwrap();
assert!(receive_data.expiration_timestamp.is_some());
let timestamp = now_timestamp + expiration as i64;
assert!(receive_data.expiration_timestamp.unwrap() - timestamp <= 1);
let receive_data = wallet
.blind_receive(
None,
None,
Some(0),
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
)
.unwrap();
assert!(receive_data.expiration_timestamp.is_none());
let min_confirmations = 2;
let receive_data = wallet
.blind_receive(
None,
None,
None,
TRANSPORT_ENDPOINTS.clone(),
min_confirmations,
)
.unwrap();
let transfer = get_test_transfer_recipient(&wallet, &receive_data.recipient_id);
let (_, batch_transfer) = get_test_transfer_related(&wallet, &transfer);
assert_eq!(batch_transfer.min_confirmations, min_confirmations);
let asset = test_issue_asset_cfa(&wallet, &online, None, None);
let asset_id = asset.asset_id;
let result = wallet.blind_receive(
Some(asset_id.clone()),
None,
None,
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
);
assert!(result.is_ok());
let now_timestamp = now().unix_timestamp();
let result = wallet.blind_receive(
Some(asset_id.clone()),
Some(amount),
Some(expiration),
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
);
assert!(result.is_ok());
let receive_data = result.unwrap();
let invoice = Invoice::new(receive_data.invoice).unwrap();
let mut invoice_data = invoice.invoice_data();
let invoice_from_data = Invoice::from_invoice_data(invoice_data.clone()).unwrap();
let approx_expiry = now_timestamp + expiration as i64;
assert_eq!(invoice.invoice_string(), invoice_from_data.invoice_string());
assert_eq!(invoice_data.recipient_id, receive_data.recipient_id);
assert_eq!(invoice_data.asset_id, Some(asset_id));
assert_eq!(invoice_data.amount, Some(amount));
assert!(invoice_data.expiration_timestamp.unwrap() - approx_expiry <= 1);
let invalid_asset_id = s!("invalid");
invoice_data.asset_id = Some(invalid_asset_id.clone());
let result = Invoice::from_invoice_data(invoice_data);
assert!(matches!(result, Err(Error::InvalidAssetID { asset_id: a }) if a == invalid_asset_id));
let result = BlindedUTXO::new(receive_data.recipient_id);
assert!(result.is_ok());
let transport_endpoints = vec![
format!("rpc://{}", "127.0.0.1:3000/json-rpc"),
format!("rpc://{}", "127.0.0.1:3001/json-rpc"),
format!("rpc://{}", "127.0.0.1:3002/json-rpc"),
];
let result = wallet.blind_receive(
None,
None,
Some(0),
transport_endpoints.clone(),
MIN_CONFIRMATIONS,
);
assert!(result.is_ok());
let transfer = get_test_transfer_recipient(&wallet, &result.unwrap().recipient_id);
let tte_data = wallet
.database
.get_transfer_transport_endpoints_data(transfer.idx)
.unwrap();
assert_eq!(tte_data.len(), transport_endpoints.len());
}
#[test]
#[parallel]
fn respect_max_allocations() {
initialize();
let (wallet, _online) = get_funded_wallet!();
let available_allocations = UTXO_NUM as u32 * MAX_ALLOCATIONS_PER_UTXO;
let mut created_allocations = 0;
for _ in 0..UTXO_NUM {
let mut txo_list: HashSet<DbTxo> = HashSet::new();
for _ in 0..MAX_ALLOCATIONS_PER_UTXO {
let receive_data = test_blind_receive(&wallet);
created_allocations += 1;
let transfer = get_test_transfer_recipient(&wallet, &receive_data.recipient_id);
let coloring = get_test_coloring(&wallet, transfer.asset_transfer_idx);
let txo = get_test_txo(&wallet, coloring.txo_idx);
txo_list.insert(txo);
}
assert_eq!(txo_list.len(), UTXO_NUM as usize);
}
assert_eq!(available_allocations, created_allocations);
let result = wallet.blind_receive(
None,
None,
None,
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
);
assert!(matches!(result, Err(Error::InsufficientAllocationSlots)));
}
#[test]
#[parallel]
fn expire() {
initialize();
let expiration = 1;
let (wallet, online) = get_funded_wallet!();
let now_timestamp = now().unix_timestamp();
let receive_data_1 = wallet
.blind_receive(
None,
None,
Some(expiration),
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
)
.unwrap();
let timestamp = now_timestamp + expiration as i64;
assert!(receive_data_1.expiration_timestamp.unwrap() - timestamp <= 1);
std::thread::sleep(std::time::Duration::from_millis(
expiration as u64 * 1000 + 2000,
));
let _asset = test_issue_asset_nia(&wallet, &online, None);
let transfer = get_test_transfer_recipient(&wallet, &receive_data_1.recipient_id);
let (transfer_data, _) = get_test_transfer_data(&wallet, &transfer);
assert_eq!(transfer_data.status, TransferStatus::Failed);
}
#[test]
#[parallel]
fn pending_outgoing_transfer_fail() {
initialize();
let amount = 66;
let (wallet, online) = get_funded_wallet!();
let (rcv_wallet, rcv_online) = get_funded_wallet!();
let asset = test_issue_asset_nia(&wallet, &online, None);
let asset_id = asset.asset_id;
let unspents = test_list_unspents(&wallet, None, false);
let unspent_issue = unspents
.iter()
.find(|u| {
u.rgb_allocations
.iter()
.any(|a| a.asset_id == Some(asset_id.clone()))
})
.unwrap();
let receive_data = test_blind_receive(&rcv_wallet);
let recipient_map = HashMap::from([(
asset_id.clone(),
vec![Recipient {
recipient_data: RecipientData::BlindedUTXO(
SecretSeal::from_str(&receive_data.recipient_id).unwrap(),
),
amount,
transport_endpoints: TRANSPORT_ENDPOINTS.clone(),
}],
)]);
let txid = test_send(&wallet, &online, &recipient_map);
assert!(!txid.is_empty());
let receive_data = test_blind_receive(&wallet);
show_unspent_colorings(&wallet, "after 1st blind");
let unspents = test_list_unspents(&wallet, None, false);
let unspent_blind_1 = unspents
.iter()
.find(|u| u.rgb_allocations.iter().any(|a| a.asset_id.is_none()))
.unwrap();
assert_ne!(unspent_issue.utxo.outpoint, unspent_blind_1.utxo.outpoint);
test_fail_transfers_blind(&wallet, &online, &receive_data.recipient_id);
test_delete_transfers(&wallet, Some(&receive_data.recipient_id), None, false);
rcv_wallet.refresh(rcv_online, None, vec![]).unwrap();
test_refresh_asset(&wallet, &online, &asset_id);
let _receive_data = test_blind_receive(&wallet);
show_unspent_colorings(&wallet, "after 2nd blind");
let unspents = test_list_unspents(&wallet, None, false);
let unspent_blind_2 = unspents
.iter()
.find(|u| u.rgb_allocations.iter().any(|a| a.asset_id.is_none()))
.unwrap();
assert_ne!(unspent_issue.utxo.outpoint, unspent_blind_2.utxo.outpoint);
}
#[test]
#[parallel]
fn fail() {
initialize();
fn blind_receive_0exp_withte(
wallet: &mut Wallet,
transport_endpoints: Vec<String>,
) -> Result<ReceiveData, Error> {
wallet.blind_receive(None, None, Some(0), transport_endpoints, MIN_CONFIRMATIONS)
}
let mut wallet = get_test_wallet(true, Some(1)); let online = test_go_online(&mut wallet, true, None);
let result = wallet.blind_receive(
None,
None,
None,
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
);
assert!(matches!(
result,
Err(Error::InsufficientBitcoins {
needed: _,
available: _
})
));
let result = BlindedUTXO::new(s!("invalid"));
assert!(matches!(
result,
Err(Error::InvalidBlindedUTXO { details: _ })
));
let result = Invoice::new(s!("invalid"));
assert!(matches!(result, Err(Error::InvalidInvoice { details: _ })));
fund_wallet(test_get_address(&wallet));
mine(false);
test_create_utxos(&wallet, &online, true, Some(1), None, FEE_RATE);
let result = wallet.blind_receive(
Some(s!("rgb1inexistent")),
None,
None,
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
);
assert!(matches!(result, Err(Error::AssetNotFound { asset_id: _ })));
let _asset = test_issue_asset_nia(&wallet, &online, None);
let result = wallet.blind_receive(
None,
None,
None,
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
);
assert!(matches!(result, Err(Error::InsufficientAllocationSlots)));
fund_wallet(test_get_address(&wallet));
test_create_utxos_default(&wallet, &online);
let transport_endpoints = vec!["malformed".to_string()];
let result = blind_receive_0exp_withte(&mut wallet, transport_endpoints);
assert!(matches!(
result,
Err(Error::InvalidTransportEndpoint { details: _ })
));
let transport_endpoints = vec![format!("unknown://{PROXY_HOST}")];
let result = blind_receive_0exp_withte(&mut wallet, transport_endpoints);
assert!(matches!(
result,
Err(Error::InvalidTransportEndpoint { details: _ })
));
let transport_endpoints = vec![format!("ws://{PROXY_HOST}")];
let result = blind_receive_0exp_withte(&mut wallet, transport_endpoints);
assert!(matches!(result, Err(Error::UnsupportedTransportType)));
let transport_endpoints = vec![];
let result = blind_receive_0exp_withte(&mut wallet, transport_endpoints);
let msg = s!("must provide at least a transport endpoint");
assert!(matches!(
result,
Err(Error::InvalidTransportEndpoints { details: m }) if m == msg
));
let transport_endpoints = vec![
format!("rpc://127.0.0.1:3000/json-rpc"),
format!("rpc://127.0.0.1:3001/json-rpc"),
format!("rpc://127.0.0.1:3002/json-rpc"),
format!("rpc://127.0.0.1:3003/json-rpc"),
];
let result = blind_receive_0exp_withte(&mut wallet, transport_endpoints);
let msg = format!(
"library supports at max {} transport endpoints",
MAX_TRANSPORT_ENDPOINTS
);
assert!(matches!(
result,
Err(Error::InvalidTransportEndpoints { details: m }) if m == msg
));
let transport_endpoints = vec![format!("rpc://{PROXY_HOST}")];
let receive_data = blind_receive_0exp_withte(&mut wallet, transport_endpoints).unwrap();
let transfer = get_test_transfer_recipient(&wallet, &receive_data.recipient_id);
let (transfer_data, _) = get_test_transfer_data(&wallet, &transfer);
let tte_data = wallet
.database
.get_transfer_transport_endpoints_data(transfer.idx)
.unwrap();
for (tte, _) in tte_data {
block_on(
transfer_transport_endpoint::Entity::delete_by_id(tte.idx)
.exec(wallet.database.get_connection()),
)
.unwrap();
}
assert_eq!(transfer_data.status, TransferStatus::WaitingCounterparty);
wallet.refresh(online, None, vec![]).unwrap();
let transfer = get_test_transfer_recipient(&wallet, &receive_data.recipient_id);
let (transfer_data, _) = get_test_transfer_data(&wallet, &transfer);
assert_eq!(transfer_data.status, TransferStatus::Failed);
let transport_endpoints = vec![
format!("rpc://{PROXY_HOST}"),
format!("rpc://{PROXY_HOST}"),
format!("rpc://{PROXY_HOST}"),
];
let result = blind_receive_0exp_withte(&mut wallet, transport_endpoints);
let msg = s!("no duplicate transport endpoints allowed");
assert!(matches!(
result,
Err(Error::InvalidTransportEndpoints { details: m }) if m == msg
));
}
#[test]
#[parallel]
fn wrong_asset_fail() {
initialize();
let amount: u64 = 66;
let (wallet_1, online_1) = get_funded_wallet!();
let (wallet_2, online_2) = get_funded_wallet!();
let asset_a = test_issue_asset_nia(&wallet_1, &online_1, None);
let asset_b = test_issue_asset_nia(&wallet_2, &online_2, None);
let receive_data_a = wallet_1
.blind_receive(
Some(asset_a.asset_id),
None,
None,
TRANSPORT_ENDPOINTS.clone(),
MIN_CONFIRMATIONS,
)
.unwrap();
let recipient_map = HashMap::from([(
asset_b.asset_id.clone(),
vec![Recipient {
amount,
recipient_data: RecipientData::BlindedUTXO(
SecretSeal::from_str(&receive_data_a.recipient_id).unwrap(),
),
transport_endpoints: TRANSPORT_ENDPOINTS.clone(),
}],
)]);
let txid = test_send(&wallet_2, &online_2, &recipient_map);
assert!(!txid.is_empty());
let rcv_transfer_a = get_test_transfer_recipient(&wallet_1, &receive_data_a.recipient_id);
let (rcv_transfer_data_a, _) = get_test_transfer_data(&wallet_1, &rcv_transfer_a);
assert_eq!(
rcv_transfer_data_a.status,
TransferStatus::WaitingCounterparty
);
wallet_1.refresh(online_1, None, vec![]).unwrap();
wallet_2.refresh(online_2, None, vec![]).unwrap();
let (rcv_transfer_data_a, _) = get_test_transfer_data(&wallet_1, &rcv_transfer_a);
assert_eq!(rcv_transfer_data_a.status, TransferStatus::Failed);
let rcv_transfers_b_result = test_list_transfers_result(&wallet_1, Some(&asset_b.asset_id));
assert!(matches!(
rcv_transfers_b_result,
Err(Error::AssetNotFound { asset_id: _ })
));
}
#[test]
#[parallel]
fn new_transport_endpoint() {
let result = TransportEndpoint::new(PROXY_ENDPOINT.clone());
assert!(result.is_ok());
let result = TransportEndpoint::new(format!("ws://{PROXY_HOST}"));
assert!(matches!(result, Err(Error::UnsupportedTransportType)));
let result = TransportEndpoint::new(PROXY_HOST.to_string());
assert!(matches!(
result,
Err(Error::InvalidTransportEndpoint { details: _ })
));
let result = TransportEndpoint::new(format!("unknown:{PROXY_HOST}"));
assert!(matches!(
result,
Err(Error::InvalidTransportEndpoint { details: _ })
));
let result = TransportEndpoint::new(format!(":rpc://{PROXY_HOST}"));
assert!(matches!(
result,
Err(Error::InvalidTransportEndpoint { details: _ })
));
}
#[test]
#[parallel]
fn multiple_receive_same_utxo() {
initialize();
let amount: u64 = 66;
let (wallet_recv, online_recv) = get_funded_noutxo_wallet!();
let (wallet_send_1, online_send_1) = get_funded_wallet!();
let (wallet_send_2, online_send_2) = get_funded_wallet!();
let created = test_create_utxos(&wallet_recv, &online_recv, false, Some(1), None, FEE_RATE);
assert_eq!(created, 1);
let unspents_recv = test_list_unspents(&wallet_recv, None, false);
assert_eq!(unspents_recv.iter().filter(|u| u.utxo.colorable).count(), 1);
let receive_data_1 = test_blind_receive(&wallet_recv);
let receive_data_2 = test_blind_receive(&wallet_recv);
let transfers_recv = test_list_transfers(&wallet_recv, None);
assert!(transfers_recv
.windows(2)
.all(|w| w[0].receive_utxo == w[1].receive_utxo));
let asset_1 = test_issue_asset_nia(&wallet_send_1, &online_send_1, None);
let recipient_map_1 = HashMap::from([(
asset_1.asset_id.clone(),
vec![Recipient {
amount,
recipient_data: RecipientData::BlindedUTXO(
SecretSeal::from_str(&receive_data_1.recipient_id).unwrap(),
),
transport_endpoints: TRANSPORT_ENDPOINTS.clone(),
}],
)]);
let txid_1 = test_send(&wallet_send_1, &online_send_1, &recipient_map_1);
assert!(!txid_1.is_empty());
let asset_2 = test_issue_asset_nia(&wallet_send_2, &online_send_2, None);
let recipient_map_2 = HashMap::from([(
asset_2.asset_id.clone(),
vec![Recipient {
amount,
recipient_data: RecipientData::BlindedUTXO(
SecretSeal::from_str(&receive_data_2.recipient_id).unwrap(),
),
transport_endpoints: TRANSPORT_ENDPOINTS.clone(),
}],
)]);
let txid_2 = test_send(&wallet_send_2, &online_send_2, &recipient_map_2);
assert!(!txid_2.is_empty());
test_refresh_all(&wallet_recv, &online_recv);
let unspents_recv = test_list_unspents(&wallet_recv, None, false);
let unspents_recv_colorable: Vec<&Unspent> =
unspents_recv.iter().filter(|u| u.utxo.colorable).collect();
assert_eq!(unspents_recv_colorable.len(), 1);
let allocations = &unspents_recv_colorable.first().unwrap().rgb_allocations;
assert_eq!(allocations.len(), 2);
assert_eq!(
allocations.first().unwrap().asset_id,
Some(asset_1.asset_id)
);
assert_eq!(allocations.last().unwrap().asset_id, Some(asset_2.asset_id));
}