|
1 | 1 | use std::sync::Arc; |
2 | 2 |
|
| 3 | +use ff::{Field, PrimeField}; |
3 | 4 | use futures::FutureExt; |
| 5 | +use group::GroupEncoding; |
4 | 6 | use json::JsonValue; |
| 7 | +use jubjub::ExtendedPoint; |
5 | 8 | use portpicker; |
| 9 | +use rand::rngs::OsRng; |
6 | 10 | use tempdir::TempDir; |
7 | 11 | use tokio::sync::{oneshot, RwLock}; |
8 | 12 | use tokio::task::JoinHandle; |
9 | 13 | use tonic::transport::{Channel, Server}; |
10 | 14 | use tonic::Request; |
11 | 15 |
|
| 16 | +use zcash_primitives::memo::Memo; |
| 17 | +use zcash_primitives::note_encryption::SaplingNoteEncryption; |
| 18 | +use zcash_primitives::primitives::{Note, Rseed, ValueCommitment}; |
| 19 | +use zcash_primitives::redjubjub::Signature; |
| 20 | +use zcash_primitives::transaction::components::{OutputDescription, GROTH_PROOF_SIZE}; |
| 21 | +use zcash_primitives::transaction::TransactionData; |
| 22 | + |
12 | 23 | use crate::blaze::test_utils::FakeCompactBlockList; |
13 | 24 | use crate::compact_formats::compact_tx_streamer_client::CompactTxStreamerClient; |
14 | 25 | use crate::compact_formats::compact_tx_streamer_server::CompactTxStreamerServer; |
15 | | -use crate::compact_formats::Empty; |
| 26 | +use crate::compact_formats::{CompactOutput, CompactTx, Empty}; |
16 | 27 | use crate::lightclient::lightclient_config::LightClientConfig; |
17 | 28 | use crate::lightclient::test_server::TestGRPCService; |
18 | 29 | use crate::lightclient::LightClient; |
@@ -248,4 +259,119 @@ async fn z_incoming_z_outgoing() { |
248 | 259 | h1.await.unwrap(); |
249 | 260 | } |
250 | 261 |
|
| 262 | +#[tokio::test] |
| 263 | +async fn multiple_incoming_same_tx() { |
| 264 | + let (data, config, ready_rx, stop_tx, h1) = create_test_server().await; |
| 265 | + |
| 266 | + ready_rx.await.unwrap(); |
| 267 | + |
| 268 | + let lc = LightClient::test_new(&config, None).await.unwrap(); |
| 269 | + let mut fcbl = FakeCompactBlockList::new(0); |
| 270 | + |
| 271 | + let extfvk1 = lc.wallet.keys().read().await.get_all_extfvks()[0].clone(); |
| 272 | + let value = 100_000; |
| 273 | + |
| 274 | + // 1. Mine 100 blocks |
| 275 | + mine_random_blocks(&mut fcbl, &data, &lc, 100).await; |
| 276 | + assert_eq!(lc.wallet.last_scanned_height().await, 100); |
| 277 | + |
| 278 | + // 2. Construct the Fake tx. |
| 279 | + let to = extfvk1.default_address().unwrap().1; |
| 280 | + |
| 281 | + // Create fake note for the account |
| 282 | + let mut ctx = CompactTx::default(); |
| 283 | + let mut td = TransactionData::new(); |
| 284 | + |
| 285 | + // Add 4 outputs |
| 286 | + for i in 0..4 { |
| 287 | + let mut rng = OsRng; |
| 288 | + let value = value + i; |
| 289 | + let note = Note { |
| 290 | + g_d: to.diversifier().g_d().unwrap(), |
| 291 | + pk_d: to.pk_d().clone(), |
| 292 | + value, |
| 293 | + rseed: Rseed::BeforeZip212(jubjub::Fr::random(rng)), |
| 294 | + }; |
| 295 | + |
| 296 | + let mut encryptor = |
| 297 | + SaplingNoteEncryption::new(None, note.clone(), to.clone(), Memo::default().into(), &mut rng); |
| 298 | + |
| 299 | + let mut rng = OsRng; |
| 300 | + let rcv = jubjub::Fr::random(&mut rng); |
| 301 | + let cv = ValueCommitment { |
| 302 | + value, |
| 303 | + randomness: rcv.clone(), |
| 304 | + }; |
| 305 | + |
| 306 | + let cmu = note.cmu(); |
| 307 | + let od = OutputDescription { |
| 308 | + cv: cv.commitment().into(), |
| 309 | + cmu: note.cmu(), |
| 310 | + ephemeral_key: ExtendedPoint::from(*encryptor.epk()), |
| 311 | + enc_ciphertext: encryptor.encrypt_note_plaintext(), |
| 312 | + out_ciphertext: encryptor.encrypt_outgoing_plaintext(&cv.commitment().into(), &cmu), |
| 313 | + zkproof: [0; GROTH_PROOF_SIZE], |
| 314 | + }; |
| 315 | + |
| 316 | + let mut cmu = vec![]; |
| 317 | + cmu.extend_from_slice(¬e.cmu().to_repr()); |
| 318 | + let mut epk = vec![]; |
| 319 | + epk.extend_from_slice(&encryptor.epk().to_bytes()); |
| 320 | + let enc_ciphertext = encryptor.encrypt_note_plaintext(); |
| 321 | + |
| 322 | + // Create a fake CompactBlock containing the note |
| 323 | + let mut cout = CompactOutput::default(); |
| 324 | + cout.cmu = cmu; |
| 325 | + cout.epk = epk; |
| 326 | + cout.ciphertext = enc_ciphertext[..52].to_vec(); |
| 327 | + ctx.outputs.push(cout); |
| 328 | + |
| 329 | + td.shielded_outputs.push(od); |
| 330 | + td.binding_sig = Signature::read(&vec![0u8; 64][..]).ok(); |
| 331 | + } |
| 332 | + |
| 333 | + let tx = td.freeze().unwrap(); |
| 334 | + ctx.hash = tx.txid().clone().0.to_vec(); |
| 335 | + |
| 336 | + // Add and mine the block |
| 337 | + fcbl.txns.push((tx.clone(), fcbl.next_height)); |
| 338 | + fcbl.add_empty_block().add_txs(vec![ctx]); |
| 339 | + mine_pending_blocks(&mut fcbl, &data, &lc).await; |
| 340 | + assert_eq!(lc.wallet.last_scanned_height().await, 101); |
| 341 | + |
| 342 | + // 2. Check the notes - that we recieved 4 notes |
| 343 | + let notes = lc.do_list_notes(true).await; |
| 344 | + for i in 0..4 { |
| 345 | + assert_eq!(notes["unspent_notes"][i]["created_in_block"].as_u64().unwrap(), 101); |
| 346 | + assert_eq!(notes["unspent_notes"][i]["value"].as_u64().unwrap(), value + i as u64); |
| 347 | + assert_eq!(notes["unspent_notes"][i]["is_change"].as_bool().unwrap(), false); |
| 348 | + assert_eq!( |
| 349 | + notes["unspent_notes"][i]["address"], |
| 350 | + lc.wallet.keys().read().await.get_all_zaddresses()[0] |
| 351 | + ); |
| 352 | + } |
| 353 | + |
| 354 | + // 3. Send a big tx, so all the value is spent |
| 355 | + let sent_value = value * 3 + 1000; |
| 356 | + mine_random_blocks(&mut fcbl, &data, &lc, 5).await; // make the funds spentable |
| 357 | + let sent_txid = lc.test_do_send(vec![(EXT_ZADDR, sent_value, None)]).await.unwrap(); |
| 358 | + |
| 359 | + // 4. Mine the sent transaction |
| 360 | + fcbl.add_pending_sends(&data).await; |
| 361 | + mine_pending_blocks(&mut fcbl, &data, &lc).await; |
| 362 | + |
| 363 | + println!("{}", lc.do_list_notes(true).await.pretty(2)); |
| 364 | + |
| 365 | + // 5. Check the notes - that we recieved 4 notes |
| 366 | + let notes = lc.do_list_notes(true).await; |
| 367 | + for i in 0..4 { |
| 368 | + assert_eq!(notes["spent_notes"][i]["spent"], sent_txid); |
| 369 | + assert_eq!(notes["spent_notes"][i]["spent_at_height"].as_u64().unwrap(), 107); |
| 370 | + } |
| 371 | + |
| 372 | + // Shutdown everything cleanly |
| 373 | + stop_tx.send(true).unwrap(); |
| 374 | + h1.await.unwrap(); |
| 375 | +} |
| 376 | + |
251 | 377 | const EXT_ZADDR: &str = "zs1va5902apnzlhdu0pw9r9q7ca8s4vnsrp2alr6xndt69jnepn2v2qrj9vg3wfcnjyks5pg65g9dc"; |
0 commit comments