routine.rs (50961B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024-2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 use std::{ 18 fmt::Debug, 19 future::Future, 20 str::FromStr, 21 sync::LazyLock, 22 time::{Duration, Instant}, 23 }; 24 25 use aws_lc_rs::signature::{Ed25519KeyPair, KeyPair as _}; 26 use axum::{Router, http::StatusCode}; 27 use jiff::{SignedDuration, Timestamp}; 28 use serde::de::DeserializeOwned; 29 use taler_api::subject::fmt_in_subject; 30 use taler_common::{ 31 api::{ 32 EddsaPublicKey, EddsaSignature, HashCode, ShortHashCode, 33 params::PageParams, 34 prepared::{ 35 PublicKeyAlg, RegistrationRequest, RegistrationResponse, TransferSubject, TransferType, 36 Unregistration, 37 }, 38 revenue::RevenueIncomingHistory, 39 wire::{ 40 IncomingBankTransaction, IncomingHistory, OutgoingHistory, TransferList, 41 TransferRequest, TransferResponse, TransferState, TransferStatus, 42 }, 43 }, 44 db::IncomingType, 45 error_code::ErrorCode, 46 types::{ 47 amount::{Amount, Currency, amount}, 48 base32::Base32, 49 payto::PaytoURI, 50 time::TalerTimestamp, 51 url, 52 }, 53 }; 54 use tokio::time::sleep; 55 56 use crate::{ 57 json, 58 server::{TestResponse, TestServer as _}, 59 }; 60 61 static UNKNOWN: LazyLock<PaytoURI> = LazyLock::new(|| { 62 PaytoURI::from_str("payto://malformed/unused?receiver-name=Malformed").unwrap() 63 }); 64 65 pub trait Page: DeserializeOwned + Debug { 66 fn ids(&self) -> Vec<i64>; 67 } 68 69 impl Page for IncomingHistory { 70 fn ids(&self) -> Vec<i64> { 71 self.incoming_transactions 72 .iter() 73 .map(|it| match it { 74 IncomingBankTransaction::Reserve { row_id, .. } 75 | IncomingBankTransaction::Wad { row_id, .. } 76 | IncomingBankTransaction::Kyc { row_id, .. } => *row_id as i64, 77 }) 78 .collect() 79 } 80 } 81 82 impl Page for OutgoingHistory { 83 fn ids(&self) -> Vec<i64> { 84 self.outgoing_transactions 85 .iter() 86 .map(|it| it.row_id as i64) 87 .collect() 88 } 89 } 90 91 impl Page for RevenueIncomingHistory { 92 fn ids(&self) -> Vec<i64> { 93 self.incoming_transactions 94 .iter() 95 .map(|it| it.row_id as i64) 96 .collect() 97 } 98 } 99 100 impl Page for TransferList { 101 fn ids(&self) -> Vec<i64> { 102 self.transfers.iter().map(|it| it.row_id as i64).collect() 103 } 104 } 105 106 pub async fn latest_id<T: Page>(router: &Router) -> i64 { 107 let res = router.get("?limit=-1").await; 108 if res.status == StatusCode::NO_CONTENT { 109 0 110 } else { 111 res.assert_ids::<T>(1)[0] 112 } 113 } 114 115 pub async fn routine_pagination<T: Page>( 116 server: &Router, 117 mut register: Tasks<impl AsyncFnMut(usize)>, 118 ) { 119 // Check supported 120 if !server.get("").await.is_implemented() { 121 return; 122 } 123 124 // Check history is following specs 125 let assert_history = 126 async |args: &str, size: usize| server.get(format!("?{args}")).await.assert_ids::<T>(size); 127 // Get latest registered id 128 let latest_id = async || latest_id::<T>(server).await; 129 130 for i in 0..20 { 131 (register.lambda)(i).await; 132 } 133 134 let id = latest_id().await; 135 136 // default 137 assert_history("", 20).await; 138 139 // forward range 140 assert_history("limit=10", 10).await; 141 assert_history("limit=10&offset=4", 10).await; 142 143 // backward range 144 assert_history("limit=-10", 10).await; 145 assert_history(&format!("limit=-10&{}", id - 4), 10).await; 146 } 147 148 pub async fn assert_time<R: Debug>(range: std::ops::Range<u128>, task: impl Future<Output = R>) { 149 let start = Instant::now(); 150 let limit = Duration::from_millis(range.end.try_into().expect("timing bound exceeds u64")); 151 if tokio::time::timeout(limit, task).await.is_err() { 152 panic!( 153 "Expected to last {range:?} ms, timed out after {} ms", 154 start.elapsed().as_millis() 155 ); 156 } 157 let elapsed = start.elapsed().as_millis(); 158 if !range.contains(&elapsed) { 159 panic!("Expected to last {range:?} got {elapsed:?}") 160 } 161 } 162 163 pub async fn routine_history<T: Page>( 164 server: &Router, 165 mut register: Tasks<impl AsyncFnMut(usize)>, 166 mut ignore: Tasks<impl AsyncFnMut(usize)>, 167 ) { 168 // Check history is following specs 169 macro_rules! assert_history { 170 ($args:expr, $size:expr) => { 171 async { 172 server 173 .get(&format!("?{}", $args)) 174 .await 175 .assert_ids::<T>($size) 176 } 177 }; 178 } 179 // Get latest registered id 180 let latest_id = async || assert_history!("limit=-1", 1).await[0]; 181 182 // Check error when no transactions 183 assert_history!("limit=7".to_owned(), 0).await; 184 185 let mut register_iter = (0..register.len).peekable(); 186 let mut ignore_iter = (0..ignore.len).peekable(); 187 while register_iter.peek().is_some() || ignore_iter.peek().is_some() { 188 if let Some(idx) = register_iter.next() { 189 (register.lambda)(idx).await 190 } 191 if let Some(idx) = ignore_iter.next() { 192 (ignore.lambda)(idx).await 193 } 194 } 195 let nb_register = register.len; 196 let nb_ignore = ignore.len; 197 let nb_total = nb_register + nb_ignore; 198 199 // Check ignored 200 assert_history!(format_args!("limit={nb_total}"), nb_register).await; 201 // Check skip ignored 202 assert_history!(format_args!("limit={nb_register}"), nb_register).await; 203 204 // Allow CI scheduling and database delays, but require completion before 205 // the long-poll timeout so broken notification delivery still fails. 206 // Check no polling when we cannot have more transactions 207 assert_time( 208 0..5000, 209 assert_history!( 210 format_args!("limit=-{}&timeout_ms=10000", nb_register + 1), 211 nb_register 212 ), 213 ) 214 .await; 215 // Check no polling when already find transactions even if less than delta 216 assert_time( 217 0..5000, 218 assert_history!( 219 format_args!("limit={}&timeout_ms=10000", nb_register + 1), 220 nb_register 221 ), 222 ) 223 .await; 224 225 // Check polling 226 let id = latest_id().await; 227 tokio::join!( 228 // Check polling succeed 229 assert_time( 230 100..5000, 231 assert_history!(format_args!("limit=2&offset={id}&timeout_ms=10000"), 1) 232 ), 233 assert_time( 234 200..5000, 235 assert_history!( 236 format_args!( 237 "limit=1&offset={}&timeout_ms=200", 238 id as usize + nb_total * 3 239 ), 240 0 241 ) 242 ), 243 async { 244 sleep(Duration::from_millis(100)).await; 245 (register.lambda)(0).await 246 } 247 ); 248 249 // Test triggers 250 for i in 0..register.len { 251 let id = latest_id().await; 252 tokio::join!( 253 // Check polling succeed 254 assert_time( 255 100..5000, 256 assert_history!(format_args!("limit=7&offset={id}&timeout_ms=10000"), 1) 257 ), 258 async { 259 sleep(Duration::from_millis(100)).await; 260 (register.lambda)(i).await 261 } 262 ); 263 } 264 265 // Test doesn't trigger 266 let id = latest_id().await; 267 tokio::join!( 268 // Check polling succeed 269 assert_time( 270 200..5000, 271 assert_history!(format_args!("limit=7&offset={id}&timeout_ms=200"), 0) 272 ), 273 async { 274 sleep(Duration::from_millis(100)).await; 275 for i in 0..ignore.len { 276 (ignore.lambda)(i).await 277 } 278 } 279 ); 280 281 routine_pagination::<T>(server, register).await; 282 } 283 284 impl TestResponse { 285 #[track_caller] 286 fn assert_ids<T: Page>(&self, size: usize) -> Vec<i64> { 287 if size == 0 { 288 self.assert_no_content(); 289 return vec![]; 290 } 291 let body = self.assert_ok_json::<T>(); 292 let page = body.ids(); 293 let params = self.query::<PageParams>().check().unwrap(); 294 295 // testing the size is like expected 296 assert_eq!(size, page.len(), "bad page length: {page:?}\n{body:?}"); 297 if params.limit < 0 { 298 // testing that the first id is at most the 'offset' query param. 299 assert!( 300 params 301 .offset 302 .map(|offset| page[0] <= offset) 303 .unwrap_or(true), 304 "bad page offset: {params:?} {page:?}" 305 ); 306 // testing that the id decreases. 307 assert!( 308 page.as_slice().is_sorted_by(|a, b| a > b), 309 "bad page order: {page:?}" 310 ) 311 } else { 312 // testing that the first id is at least the 'offset' query param. 313 assert!( 314 params 315 .offset 316 .map(|offset| page[0] >= offset) 317 .unwrap_or(true), 318 "bad page offset: {params:?} {page:?}" 319 ); 320 // testing that the id increases. 321 assert!(page.as_slice().is_sorted(), "bad page order: {page:?}") 322 } 323 page 324 } 325 } 326 327 // Get currency from config 328 async fn get_currency(server: &Router) -> Currency { 329 let config = server 330 .get("/config") 331 .await 332 .assert_ok_json::<serde_json::Value>(); 333 let currency = config["currency"].as_str().unwrap(); 334 Currency::from_str(currency).unwrap() 335 } 336 337 /// Test standard behavior of the transfer endpoints 338 pub async fn transfer_routine( 339 wire_gateway: &Router, 340 default_status: TransferState, 341 credit_account: &PaytoURI, 342 ) { 343 let currency = &get_currency(wire_gateway).await; 344 let default_amount = amount(format!("{currency}:42")); 345 let request_uid = HashCode::rand(); 346 let wtid = ShortHashCode::rand(); 347 let valid_req = json!({ 348 "request_uid": request_uid, 349 "amount": default_amount, 350 "exchange_base_url": "http://exchange.taler/", 351 "wtid": wtid, 352 "credit_account": credit_account, 353 }); 354 355 // Check empty db 356 { 357 wire_gateway.get("/transfers").await.assert_no_content(); 358 wire_gateway 359 .get(format!("/transfers?status={}", default_status.as_ref())) 360 .await 361 .assert_no_content(); 362 } 363 364 // TODO check subject formatting 365 366 let routine = async |req: &TransferRequest| { 367 // Check OK 368 let first = wire_gateway 369 .post("/transfer") 370 .json(req) 371 .await 372 .assert_ok_json::<TransferResponse>(); 373 // Check idempotent 374 let second = wire_gateway 375 .post("/transfer") 376 .json(req) 377 .await 378 .assert_ok_json::<TransferResponse>(); 379 assert_eq!(first.row_id, second.row_id); 380 assert_eq!(first.timestamp, second.timestamp); 381 382 // Check by id 383 let tx = wire_gateway 384 .get(format!("/transfers/{}", first.row_id)) 385 .await 386 .assert_ok_json::<TransferStatus>(); 387 assert_eq!(default_status, tx.status); 388 assert_eq!(default_amount, tx.amount); 389 assert_eq!("http://exchange.taler/", tx.exchange_base_url); 390 assert_eq!(req.wtid, tx.wtid); 391 assert_eq!(first.timestamp, tx.timestamp); 392 assert_eq!(req.metadata, tx.metadata); 393 assert_eq!(credit_account, &tx.credit_account); 394 395 // Check page 396 let list = wire_gateway 397 .get("/transfers?limit=-1") 398 .await 399 .assert_ok_json::<TransferList>(); 400 let tx = &list.transfers[0]; 401 assert_eq!(first.row_id, tx.row_id); 402 assert_eq!(default_status, tx.status); 403 assert_eq!(default_amount, tx.amount); 404 assert_eq!(first.timestamp, tx.timestamp); 405 assert_eq!(credit_account, &tx.credit_account); 406 }; 407 408 let req = TransferRequest { 409 request_uid, 410 amount: default_amount, 411 exchange_base_url: url("http://exchange.taler/"), 412 metadata: None, 413 wtid, 414 credit_account: credit_account.clone(), 415 }; 416 // Simple 417 routine(&req).await; 418 // With metadata 419 routine(&TransferRequest { 420 request_uid: HashCode::rand(), 421 wtid: ShortHashCode::rand(), 422 metadata: Some("test:medatata".into()), 423 ..req 424 }) 425 .await; 426 427 // Check create transfer errors 428 { 429 // Check request uid reuse 430 wire_gateway 431 .post("/transfer") 432 .json(json!(valid_req + { 433 "wtid": ShortHashCode::rand() 434 })) 435 .await 436 .assert_error(ErrorCode::BANK_TRANSFER_REQUEST_UID_REUSED); 437 // Check wtid reuse 438 wire_gateway 439 .post("/transfer") 440 .json(json!(valid_req + { 441 "request_uid": HashCode::rand(), 442 })) 443 .await 444 .assert_error(ErrorCode::BANK_TRANSFER_WTID_REUSED); 445 446 // Check currency mismatch 447 wire_gateway 448 .post("/transfer") 449 .json(json!(valid_req + { 450 "amount": "BAD:42" 451 })) 452 .await 453 .assert_error(ErrorCode::GENERIC_CURRENCY_MISMATCH); 454 455 // Base Base32 456 wire_gateway 457 .post("/transfer") 458 .json(json!(valid_req + { 459 "wtid": "I love chocolate" 460 })) 461 .await 462 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 463 wire_gateway 464 .post("/transfer") 465 .json(json!(valid_req + { 466 "wtid": Base32::<31>::rand() 467 })) 468 .await 469 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 470 wire_gateway 471 .post("/transfer") 472 .json(json!(valid_req + { 473 "request_uid": "I love chocolate" 474 })) 475 .await 476 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 477 wire_gateway 478 .post("/transfer") 479 .json(json!(valid_req + { 480 "request_uid": Base32::<65>::rand() 481 })) 482 .await 483 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 484 485 // Missing receiver-name 486 let res = wire_gateway 487 .post("/transfer") 488 .json(json!(valid_req + { 489 "credit_account": credit_account.as_ref().as_str().split('?').next().unwrap() 490 })) 491 .await; 492 if !res.status.is_success() { 493 res.assert_error(ErrorCode::GENERIC_PAYTO_URI_MALFORMED); 494 } 495 496 // Unsupported payto kind 497 wire_gateway 498 .post("/transfer") 499 .json(json!(valid_req + { "credit_account": *UNKNOWN })) 500 .await 501 .assert_error(ErrorCode::GENERIC_PAYTO_URI_MALFORMED); 502 // Malformed payto 503 wire_gateway 504 .post("/transfer") 505 .json(json!(valid_req + { "credit_account": "http://email@test.com" })) 506 .await 507 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 508 509 // Bad base URL 510 for base_url in [ 511 "not-a-url", 512 "file://not.http.com/", 513 "no.transport.com/", 514 "https://not.a/base/url", 515 ] { 516 wire_gateway 517 .post("/transfer") 518 .json(&json!(valid_req + { "exchange_base_url": base_url })) 519 .await 520 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 521 } 522 523 // Malformed metadata 524 for metadata in ["bad_id", "bad id", "bad@id.com", &"A".repeat(41)] { 525 wire_gateway 526 .post("/transfer") 527 .json(&json!(valid_req + { "metadata": metadata })) 528 .await 529 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 530 } 531 } 532 533 // Check transfer by id errors 534 { 535 // Check unknown transaction 536 wire_gateway 537 .get("/transfers/42") 538 .await 539 .assert_error(ErrorCode::BANK_TRANSACTION_NOT_FOUND); 540 } 541 542 // Check transfer page 543 { 544 for _ in 0..4 { 545 wire_gateway 546 .post("/transfer") 547 .json(&json!(valid_req + { 548 "request_uid": HashCode::rand(), 549 "wtid": ShortHashCode::rand(), 550 })) 551 .await 552 .assert_ok_json::<TransferResponse>(); 553 } 554 { 555 let list = wire_gateway 556 .get("/transfers") 557 .await 558 .assert_ok_json::<TransferList>(); 559 assert_eq!(list.transfers.len(), 6); 560 assert_eq!( 561 list, 562 wire_gateway 563 .get(format!("/transfers?status={}", default_status.as_ref())) 564 .await 565 .assert_ok_json::<TransferList>() 566 ) 567 } 568 569 // Pagination test 570 routine_pagination::<TransferList>( 571 &wire_gateway.suffix("/transfers"), 572 crate::tasks!({ 573 wire_gateway 574 .post("/transfer") 575 .json(json!({ 576 "request_uid": HashCode::rand(), 577 "amount": amount(format!("{currency}:0.1")), 578 "exchange_base_url": url("http://exchange.taler"), 579 "wtid": ShortHashCode::rand(), 580 "credit_account": credit_account, 581 })) 582 .await 583 .assert_ok_json::<TransferResponse>(); 584 }), 585 ) 586 .await; 587 } 588 } 589 590 async fn add_incoming_routine( 591 wire_gateway: &Router, 592 prepared_transfer: &Router, 593 currency: &Currency, 594 kind: IncomingType, 595 debit_acount: &PaytoURI, 596 credit_account: &PaytoURI, 597 ) { 598 let (path, key) = match kind { 599 IncomingType::reserve => ("/admin/add-incoming", "reserve_pub"), 600 IncomingType::kyc => ("/admin/add-kycauth", "account_pub"), 601 IncomingType::map => ("/admin/add-mapped", "authorization_pub"), 602 }; 603 let key_pair = Ed25519KeyPair::generate().unwrap(); 604 let pub_key = EddsaPublicKey::try_from(key_pair.public_key().as_ref()).unwrap(); 605 // Valid 606 let req = RegistrationRequest { 607 credit_account: credit_account.clone(), 608 r#type: TransferType::reserve, 609 recurrent: false, 610 credit_amount: Amount::new(currency, 44, 0), 611 alg: PublicKeyAlg::EdDSA, 612 account_pub: pub_key, 613 authorization_pub: pub_key, 614 authorization_sig: EddsaSignature::ZEROED, 615 } 616 .signed(&key_pair); 617 618 prepared_transfer 619 .post("/registration") 620 .json(&req) 621 .await 622 .assert_ok_json::<RegistrationResponse>(); 623 let valid_req = json!({ 624 "amount": format!("{currency}:44"), 625 key: pub_key, 626 "debit_account": debit_acount, 627 }); 628 629 // Check OK 630 wire_gateway.post(path).json(&valid_req).await.assert_ok(); 631 632 match kind { 633 IncomingType::reserve => { 634 // Trigger conflict due to reused reserve_pub 635 wire_gateway 636 .post(path) 637 .json(&json!(valid_req + { 638 "amount": format!("{currency}:44.1"), 639 })) 640 .await 641 .assert_error(ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT) 642 } 643 IncomingType::kyc => { 644 // Non conflict on reuse 645 wire_gateway.post(path).json(&valid_req).await.assert_ok(); 646 } 647 IncomingType::map => { 648 // Trigger conflict due to reused authorization_pub 649 wire_gateway 650 .post(path) 651 .json(&valid_req) 652 .await 653 .assert_error(ErrorCode::BANK_TRANSFER_MAPPING_REUSED); 654 // Trigger conflict due to unknown authorization_pub 655 wire_gateway 656 .post(path) 657 .json(&json!(valid_req + { 658 key: EddsaPublicKey::rand() 659 })) 660 .await 661 .assert_error(ErrorCode::BANK_TRANSFER_MAPPING_UNKNOWN); 662 } 663 } 664 665 // Currency mismatch 666 wire_gateway 667 .post(path) 668 .json(&json!(valid_req + { "amount": "BAD:33" })) 669 .await 670 .assert_error(ErrorCode::GENERIC_CURRENCY_MISMATCH); 671 672 // Bad BASE32 reserve_pub 673 wire_gateway 674 .post(path) 675 .json(json!(valid_req + { key: "I love chocolate" })) 676 .await 677 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 678 wire_gateway 679 .post(path) 680 .json(json!(valid_req + { key: Base32::<31>::rand() })) 681 .await 682 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 683 684 // Unsupported payto kind 685 wire_gateway 686 .post(path) 687 .json(json!(valid_req + { "debit_account": *UNKNOWN })) 688 .await 689 .assert_error(ErrorCode::GENERIC_PAYTO_URI_MALFORMED); 690 691 // Malformed payto 692 wire_gateway 693 .post(path) 694 .json(json!(valid_req + { "debit_account": "http://email@test.com" })) 695 .await 696 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 697 } 698 699 pub struct Tasks<F: AsyncFnMut(usize)> { 700 pub len: usize, 701 pub lambda: F, 702 } 703 704 #[macro_export] 705 macro_rules! tasks { 706 // Create new 707 ( $( $(if $cond:expr =>)? $body:block ),* $(,)? ) => { 708 $crate::tasks!(@build 0usize; 709 $( $(if $cond =>)? $body ),* 710 ) 711 }; 712 713 // Append to existing 714 ( $existing:expr ; $( $(if $cond:expr =>)? $body:block ),* $(,)? ) => {{ 715 let mut existing = $existing; 716 let mut extra = $crate::tasks![ 717 $( $(if $cond =>)? $body ),* 718 ]; 719 720 $crate::routine::Tasks { 721 len: existing.len + extra.len, 722 lambda: async move |i: usize| { 723 if existing.len == 0 && extra.len == 0 { 724 return; 725 } 726 727 let i = i % (existing.len + extra.len); 728 729 if i < existing.len { 730 (existing.lambda)(i).await; 731 } else { 732 (extra.lambda)(i - existing.len).await; 733 } 734 } 735 } 736 }}; 737 738 // Internal builder 739 (@build $idx:expr; $( $(if $cond:expr =>)? $body:block ),* ) => {{ 740 let conditions = [ $( true $( && $cond )? ),* ]; 741 let len = conditions.iter().filter(|&&x| x).count(); 742 743 $crate::routine::Tasks { 744 len, 745 lambda: async move |i: usize| { 746 if len == 0 { 747 return; 748 } 749 750 let i = i % len; 751 let mut current = 0usize; 752 753 $crate::tasks!( 754 @dispatch i, current, conditions, 0usize; 755 $( $(if $cond =>)? $body ),* 756 ); 757 758 let _ = (i, &mut current); // suppress lints 759 760 unreachable!() 761 } 762 } 763 }}; 764 765 // Recursive dispatcher 766 (@dispatch $i:expr, $current:ident, $conditions:ident, $idx:expr;) => {}; 767 768 (@dispatch 769 $i:expr, 770 $current:ident, 771 $conditions:ident, 772 $idx:expr; 773 $(if $cond:expr =>)? $body:block 774 $(, $($rest:tt)*)? 775 ) => {{ 776 if $conditions[$idx] { 777 if $i == $current { 778 (async $body).await; 779 return; 780 } 781 $current += 1; 782 } 783 784 $crate::tasks!( 785 @dispatch 786 $i, 787 $current, 788 $conditions, 789 $idx + 1usize; 790 $($($rest)*)? 791 ); 792 }}; 793 } 794 795 /// Test standard behavior of the revenue endpoints 796 pub async fn revenue_routine( 797 wire_gateway: &Router, 798 revenue_api: &Router, 799 debit_acount: &PaytoURI, 800 register: Tasks<impl AsyncFnMut(usize)>, 801 ignore: Tasks<impl AsyncFnMut(usize)>, 802 ) { 803 let currency = &get_currency(revenue_api).await; 804 routine_history::<RevenueIncomingHistory>( 805 &revenue_api.suffix("/history"), 806 tasks!(register; 807 { 808 wire_gateway 809 .post("/admin/add-incoming") 810 .json(json!({ 811 "amount": format!("{currency}:1"), 812 "reserve_pub": EddsaPublicKey::rand(), 813 "debit_account": debit_acount, 814 })) 815 .await 816 .assert_ok_json::<TransferResponse>(); 817 }, 818 { 819 wire_gateway 820 .post("/admin/add-kycauth") 821 .json(json!({ 822 "amount": format!("{currency}:2"), 823 "account_pub": EddsaPublicKey::rand(), 824 "debit_account": debit_acount, 825 })) 826 .await 827 .assert_ok_json::<TransferResponse>(); 828 } 829 ), 830 ignore, 831 ) 832 .await; 833 } 834 835 /// Test standard behavior of the outgoing history endpoint 836 pub async fn out_history_routine( 837 wire_gateway: &Router, 838 register: Tasks<impl AsyncFnMut(usize)>, 839 ignore: Tasks<impl AsyncFnMut(usize)>, 840 ) { 841 routine_history::<OutgoingHistory>(&wire_gateway.suffix("/history/outgoing"), register, ignore) 842 .await; 843 } 844 845 /// Test standard behavior of the incoming history endpoint 846 pub async fn in_history_routine( 847 wire_gateway: &Router, 848 prepared_transfer: &Router, 849 debit_account: &PaytoURI, 850 credit_account: &PaytoURI, 851 register: Tasks<impl AsyncFnMut(usize)>, 852 ignored: Tasks<impl AsyncFnMut(usize)>, 853 ) { 854 let currency = &get_currency(wire_gateway).await; 855 let mut key = Ed25519KeyPair::generate().unwrap(); 856 857 routine_history::<IncomingHistory>( 858 &wire_gateway.suffix("/history/incoming"), 859 tasks!(register; 860 { 861 wire_gateway 862 .post("/admin/add-incoming") 863 .json(json!({ 864 "amount": format!("{currency}:1"), 865 "reserve_pub": EddsaPublicKey::rand(), 866 "debit_account": debit_account, 867 })) 868 .await 869 .assert_ok_json::<TransferResponse>(); 870 }, 871 { 872 key = Ed25519KeyPair::generate().unwrap(); 873 let auth_pub = EddsaPublicKey::try_from(key.public_key().as_ref()).unwrap(); 874 let reserve_pub = EddsaPublicKey::rand(); 875 let amount = Amount::new(currency, 2, 0); 876 prepared_transfer 877 .post("/registration") 878 .json( 879 RegistrationRequest { 880 credit_account: credit_account.clone(), 881 r#type: TransferType::reserve, 882 recurrent: true, 883 credit_amount: amount, 884 alg: PublicKeyAlg::EdDSA, 885 account_pub: reserve_pub, 886 authorization_pub: auth_pub, 887 authorization_sig: EddsaSignature::ZEROED, 888 } 889 .signed(&key) 890 ) 891 .await 892 .assert_ok_json::<RegistrationResponse>(); 893 wire_gateway 894 .post("/admin/add-mapped") 895 .json(json!({ 896 "amount": amount, 897 "authorization_pub": auth_pub, 898 "debit_account": debit_account, 899 })) 900 .await 901 .assert_ok_json::<TransferResponse>(); 902 wire_gateway 903 .post("/admin/add-mapped") 904 .json(json!({ 905 "amount": amount, 906 "authorization_pub": auth_pub, 907 "debit_account": debit_account, 908 })) 909 .await 910 .assert_ok_json::<TransferResponse>(); 911 }, 912 { 913 let auth_pub = EddsaPublicKey::try_from(key.public_key().as_ref()).unwrap(); 914 let reserve_pub = EddsaPublicKey::rand(); 915 prepared_transfer 916 .post("/registration") 917 .json( 918 RegistrationRequest { 919 credit_account: credit_account.clone(), 920 r#type: TransferType::reserve, 921 recurrent: true, 922 credit_amount: Amount::new(currency, 3, 0), 923 alg: PublicKeyAlg::EdDSA, 924 account_pub: reserve_pub, 925 authorization_pub: auth_pub, 926 authorization_sig: EddsaSignature::ZEROED, 927 } 928 .signed(&key) 929 ) 930 .await 931 .assert_ok_json::<RegistrationResponse>(); 932 }, 933 { 934 wire_gateway 935 .post("/admin/add-kycauth") 936 .json(json!({ 937 "amount": format!("{currency}:4"), 938 "account_pub": EddsaPublicKey::rand(), 939 "debit_account": debit_account, 940 })) 941 .await 942 .assert_ok_json::<TransferResponse>(); 943 }, 944 { 945 key = Ed25519KeyPair::generate().unwrap(); 946 let auth_pub = EddsaPublicKey::try_from(key.public_key().as_ref()).unwrap(); 947 let account_pub = EddsaPublicKey::rand(); 948 let amount = Amount::new(currency, 5, 0); 949 prepared_transfer 950 .post("/registration") 951 .json( 952 RegistrationRequest { 953 credit_account: credit_account.clone(), 954 r#type: TransferType::kyc, 955 recurrent: true, 956 credit_amount: amount, 957 alg: PublicKeyAlg::EdDSA, 958 account_pub, 959 authorization_pub: auth_pub, 960 authorization_sig: EddsaSignature::ZEROED, 961 } 962 .signed(&key) 963 ) 964 .await 965 .assert_ok_json::<RegistrationResponse>(); 966 wire_gateway 967 .post("/admin/add-mapped") 968 .json(json!({ 969 "amount": amount, 970 "authorization_pub": auth_pub, 971 "debit_account": debit_account, 972 })) 973 .await 974 .assert_ok_json::<TransferResponse>(); 975 wire_gateway 976 .post("/admin/add-mapped") 977 .json(json!({ 978 "amount": amount, 979 "authorization_pub": auth_pub, 980 "debit_account": debit_account, 981 })) 982 .await 983 .assert_ok_json::<TransferResponse>(); 984 }, 985 { 986 let auth_pub = EddsaPublicKey::try_from(key.public_key().as_ref()).unwrap(); 987 let account_pub = EddsaPublicKey::rand(); 988 prepared_transfer 989 .post("/registration") 990 .json( 991 RegistrationRequest { 992 credit_account: credit_account.clone(), 993 r#type: TransferType::kyc, 994 recurrent: true, 995 credit_amount: Amount::new(currency, 6, 0), 996 alg: PublicKeyAlg::EdDSA, 997 account_pub, 998 authorization_pub: auth_pub, 999 authorization_sig: EddsaSignature::ZEROED, 1000 } 1001 .signed(&key) 1002 ) 1003 .await 1004 .assert_ok_json::<RegistrationResponse>(); 1005 } 1006 ), 1007 ignored, 1008 ) 1009 .await; 1010 } 1011 1012 /// Test standard behavior of the admin add incoming endpoints 1013 pub async fn admin_add_incoming_routine( 1014 wire_gateway: &Router, 1015 prepared_transfer: &Router, 1016 debit_acount: &PaytoURI, 1017 credit_account: &PaytoURI, 1018 ) { 1019 let currency = &get_currency(wire_gateway).await; 1020 for kind in IncomingType::entries { 1021 add_incoming_routine( 1022 wire_gateway, 1023 prepared_transfer, 1024 currency, 1025 *kind, 1026 debit_acount, 1027 credit_account, 1028 ) 1029 .await; 1030 } 1031 } 1032 1033 #[derive(Debug, PartialEq, Eq)] 1034 pub enum Status { 1035 Simple, 1036 Pending, 1037 Bounced, 1038 Incomplete, 1039 Reserve(EddsaPublicKey), 1040 Kyc(EddsaPublicKey), 1041 } 1042 1043 /// Test standard registration behavior of the registration endpoints 1044 pub async fn registration_routine<F1: Future<Output = Vec<Status>>>( 1045 wire_gateway: &Router, 1046 prepared_transfer: &Router, 1047 debit_acount: &PaytoURI, 1048 credit_account: &PaytoURI, 1049 unknown_account: &PaytoURI, 1050 mut in_status: impl FnMut() -> F1, 1051 ) { 1052 pub use Status::*; 1053 let mut check_in = async |state: &[Status]| { 1054 let current = in_status().await; 1055 pretty_assertions::assert_eq!(state, current); 1056 }; 1057 1058 let currency = &get_currency(wire_gateway).await; 1059 let amount = amount(format!("{currency}:42")); 1060 let key_pair1 = Ed25519KeyPair::generate().unwrap(); 1061 let auth_pub1 = EddsaPublicKey::try_from(key_pair1.public_key().as_ref()).unwrap(); 1062 let req = RegistrationRequest { 1063 credit_account: credit_account.clone(), 1064 r#type: TransferType::reserve, 1065 recurrent: false, 1066 credit_amount: amount, 1067 alg: PublicKeyAlg::EdDSA, 1068 account_pub: auth_pub1, 1069 authorization_pub: auth_pub1, 1070 authorization_sig: EddsaSignature::ZEROED, 1071 }; 1072 1073 let register = async |auth_pub: &EddsaPublicKey| { 1074 wire_gateway 1075 .post("/admin/add-mapped") 1076 .json(json!({ 1077 "amount": format!("{currency}:42"), 1078 "authorization_pub": auth_pub, 1079 "debit_account": debit_acount, 1080 })) 1081 .await 1082 }; 1083 1084 /* ----- Registration ----- */ 1085 let routine = async |ty: TransferType, 1086 account_pub: EddsaPublicKey, 1087 recurrent: bool, 1088 fmt: IncomingType| { 1089 let req = RegistrationRequest { 1090 r#type: ty, 1091 account_pub, 1092 recurrent, 1093 ..req.clone() 1094 } 1095 .signed(&key_pair1); 1096 // Valid 1097 let res = prepared_transfer 1098 .post("/registration") 1099 .json(&req) 1100 .await 1101 .assert_ok_json::<RegistrationResponse>(); 1102 1103 // Idempotent 1104 assert_eq!( 1105 res, 1106 prepared_transfer 1107 .post("/registration") 1108 .json(&req) 1109 .await 1110 .assert_ok_json::<RegistrationResponse>() 1111 ); 1112 1113 assert!(!res.subjects.is_empty()); 1114 1115 for sub in res.subjects { 1116 if let TransferSubject::Simple { subject, .. } = sub { 1117 assert_eq!(subject, fmt_in_subject(fmt, &auth_pub1).to_string()); 1118 }; 1119 } 1120 }; 1121 for ty in [TransferType::reserve, TransferType::kyc] { 1122 routine(ty, auth_pub1, false, ty.into()).await; 1123 routine(ty, auth_pub1, true, IncomingType::map).await; 1124 } 1125 1126 let acc_pub1 = EddsaPublicKey::rand(); 1127 for ty in [TransferType::reserve, TransferType::kyc] { 1128 routine(ty, acc_pub1, false, IncomingType::map).await; 1129 routine(ty, acc_pub1, true, IncomingType::map).await; 1130 } 1131 1132 // Bad signature 1133 prepared_transfer 1134 .post("/registration") 1135 .json(&req) 1136 .await 1137 .assert_error(ErrorCode::BANK_BAD_SIGNATURE); 1138 1139 // Unknown account 1140 prepared_transfer 1141 .post("/registration") 1142 .json( 1143 RegistrationRequest { 1144 r#credit_account: unknown_account.clone(), 1145 ..req.clone() 1146 } 1147 .signed(&key_pair1), 1148 ) 1149 .await 1150 .assert_error(ErrorCode::BANK_UNKNOWN_CREDITOR); 1151 1152 // Unsupported payto kind 1153 prepared_transfer 1154 .post("/registration") 1155 .json( 1156 RegistrationRequest { 1157 r#credit_account: UNKNOWN.clone(), 1158 ..req.clone() 1159 } 1160 .signed(&key_pair1), 1161 ) 1162 .await 1163 .assert_error(ErrorCode::GENERIC_PAYTO_URI_MALFORMED); 1164 1165 // Malformed payto 1166 prepared_transfer 1167 .post("/registration") 1168 .json( 1169 RegistrationRequest { 1170 r#credit_account: unsafe { PaytoURI::from_raw("http://email@test.com") }, 1171 ..req.clone() 1172 } 1173 .signed(&key_pair1), 1174 ) 1175 .await 1176 .assert_error(ErrorCode::GENERIC_JSON_INVALID); 1177 1178 // Reserve pub reuse 1179 prepared_transfer 1180 .post("/registration") 1181 .json( 1182 RegistrationRequest { 1183 account_pub: acc_pub1, 1184 ..req.clone() 1185 } 1186 .signed(&key_pair1), 1187 ) 1188 .await 1189 .assert_ok_json::<RegistrationResponse>(); 1190 { 1191 let key_pair = Ed25519KeyPair::generate().unwrap(); 1192 let auth_pub = EddsaPublicKey::try_from(key_pair.public_key().as_ref()).unwrap(); 1193 prepared_transfer 1194 .post("/registration") 1195 .json( 1196 RegistrationRequest { 1197 account_pub: acc_pub1, 1198 authorization_pub: auth_pub, 1199 ..req.clone() 1200 } 1201 .signed(&key_pair), 1202 ) 1203 .await 1204 .assert_error(ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT); 1205 } 1206 1207 // Non recurrent accept one then bounce 1208 prepared_transfer 1209 .post("/registration") 1210 .json( 1211 RegistrationRequest { 1212 account_pub: acc_pub1, 1213 ..req.clone() 1214 } 1215 .signed(&key_pair1), 1216 ) 1217 .await 1218 .assert_ok_json::<RegistrationResponse>(); 1219 register(&auth_pub1) 1220 .await 1221 .assert_ok_json::<TransferResponse>(); 1222 check_in(&[Reserve(acc_pub1)]).await; 1223 register(&auth_pub1) 1224 .await 1225 .assert_error(ErrorCode::BANK_TRANSFER_MAPPING_REUSED); 1226 1227 // Again without using mapping 1228 let acc_pub2 = EddsaPublicKey::rand(); 1229 prepared_transfer 1230 .post("/registration") 1231 .json( 1232 RegistrationRequest { 1233 account_pub: acc_pub2, 1234 ..req.clone() 1235 } 1236 .signed(&key_pair1), 1237 ) 1238 .await 1239 .assert_ok_json::<RegistrationResponse>(); 1240 wire_gateway 1241 .post("/admin/add-incoming") 1242 .json(json!({ 1243 "amount": amount, 1244 "reserve_pub": acc_pub2, 1245 "debit_account": debit_acount, 1246 })) 1247 .await 1248 .assert_ok(); 1249 register(&auth_pub1) 1250 .await 1251 .assert_error(ErrorCode::BANK_TRANSFER_MAPPING_REUSED); 1252 check_in(&[Reserve(acc_pub1), Reserve(acc_pub2)]).await; 1253 1254 // Recurrent accept one and delay others 1255 let acc_pub3 = EddsaPublicKey::rand(); 1256 prepared_transfer 1257 .post("/registration") 1258 .json( 1259 RegistrationRequest { 1260 account_pub: acc_pub3, 1261 recurrent: true, 1262 ..req.clone() 1263 } 1264 .signed(&key_pair1), 1265 ) 1266 .await 1267 .assert_ok_json::<RegistrationResponse>(); 1268 for _ in 0..5 { 1269 register(&auth_pub1) 1270 .await 1271 .assert_ok_json::<TransferResponse>(); 1272 } 1273 check_in(&[ 1274 Reserve(acc_pub1), 1275 Reserve(acc_pub2), 1276 Reserve(acc_pub3), 1277 Pending, 1278 Pending, 1279 Pending, 1280 Pending, 1281 ]) 1282 .await; 1283 1284 // Complete pending on recurrent update 1285 let acc_pub4 = EddsaPublicKey::rand(); 1286 prepared_transfer 1287 .post("/registration") 1288 .json( 1289 RegistrationRequest { 1290 r#type: TransferType::kyc, 1291 account_pub: acc_pub4, 1292 recurrent: true, 1293 ..req.clone() 1294 } 1295 .signed(&key_pair1), 1296 ) 1297 .await 1298 .assert_ok_json::<RegistrationResponse>(); 1299 prepared_transfer 1300 .post("/registration") 1301 .json( 1302 RegistrationRequest { 1303 account_pub: acc_pub4, 1304 recurrent: true, 1305 ..req.clone() 1306 } 1307 .signed(&key_pair1), 1308 ) 1309 .await 1310 .assert_ok_json::<RegistrationResponse>(); 1311 check_in(&[ 1312 Reserve(acc_pub1), 1313 Reserve(acc_pub2), 1314 Reserve(acc_pub3), 1315 Kyc(acc_pub4), 1316 Reserve(acc_pub4), 1317 Pending, 1318 Pending, 1319 ]) 1320 .await; 1321 1322 // Kyc key reuse keep pending ones 1323 wire_gateway 1324 .post("/admin/add-kycauth") 1325 .json(json!({ 1326 "amount": amount, 1327 "account_pub": acc_pub4, 1328 "debit_account": debit_acount, 1329 })) 1330 .await 1331 .assert_ok_json::<TransferResponse>(); 1332 check_in(&[ 1333 Reserve(acc_pub1), 1334 Reserve(acc_pub2), 1335 Reserve(acc_pub3), 1336 Kyc(acc_pub4), 1337 Reserve(acc_pub4), 1338 Pending, 1339 Pending, 1340 Kyc(acc_pub4), 1341 ]) 1342 .await; 1343 1344 // Switching to non recurrent cancel pending 1345 let auth_pair = Ed25519KeyPair::generate().unwrap(); 1346 let auth_pub2 = EddsaPublicKey::try_from(auth_pair.public_key().as_ref()).unwrap(); 1347 prepared_transfer 1348 .post("/registration") 1349 .json( 1350 RegistrationRequest { 1351 account_pub: auth_pub2, 1352 authorization_pub: auth_pub2, 1353 recurrent: true, 1354 ..req.clone() 1355 } 1356 .signed(&auth_pair), 1357 ) 1358 .await 1359 .assert_ok_json::<RegistrationResponse>(); 1360 for _ in 0..3 { 1361 register(&auth_pub2) 1362 .await 1363 .assert_ok_json::<TransferResponse>(); 1364 } 1365 check_in(&[ 1366 Reserve(acc_pub1), 1367 Reserve(acc_pub2), 1368 Reserve(acc_pub3), 1369 Kyc(acc_pub4), 1370 Reserve(acc_pub4), 1371 Pending, 1372 Pending, 1373 Kyc(acc_pub4), 1374 Reserve(auth_pub2), 1375 Pending, 1376 Pending, 1377 ]) 1378 .await; 1379 prepared_transfer 1380 .post("/registration") 1381 .json( 1382 RegistrationRequest { 1383 r#type: TransferType::kyc, 1384 account_pub: auth_pub2, 1385 authorization_pub: auth_pub2, 1386 recurrent: false, 1387 ..req.clone() 1388 } 1389 .signed(&auth_pair), 1390 ) 1391 .await 1392 .assert_ok_json::<RegistrationResponse>(); 1393 check_in(&[ 1394 Reserve(acc_pub1), 1395 Reserve(acc_pub2), 1396 Reserve(acc_pub3), 1397 Kyc(acc_pub4), 1398 Reserve(acc_pub4), 1399 Pending, 1400 Pending, 1401 Kyc(acc_pub4), 1402 Reserve(auth_pub2), 1403 Bounced, 1404 Bounced, 1405 ]) 1406 .await; 1407 1408 // Recurrent reserve simple subject 1409 let acc_pub5 = EddsaPublicKey::rand(); 1410 prepared_transfer 1411 .post("/registration") 1412 .json( 1413 RegistrationRequest { 1414 account_pub: acc_pub5, 1415 authorization_pub: auth_pub2, 1416 recurrent: true, 1417 ..req.clone() 1418 } 1419 .signed(&auth_pair), 1420 ) 1421 .await 1422 .assert_ok_json::<RegistrationResponse>(); 1423 wire_gateway 1424 .post("/admin/add-incoming") 1425 .json(json!({ 1426 "amount": amount, 1427 "reserve_pub": acc_pub5, 1428 "debit_account": debit_acount, 1429 })) 1430 .await 1431 .assert_ok(); 1432 register(&auth_pub2) 1433 .await 1434 .assert_ok_json::<TransferResponse>(); 1435 check_in(&[ 1436 Reserve(acc_pub1), 1437 Reserve(acc_pub2), 1438 Reserve(acc_pub3), 1439 Kyc(acc_pub4), 1440 Reserve(acc_pub4), 1441 Pending, 1442 Pending, 1443 Kyc(acc_pub4), 1444 Reserve(auth_pub2), 1445 Bounced, 1446 Bounced, 1447 Reserve(acc_pub5), 1448 Pending, 1449 ]) 1450 .await; 1451 1452 // Recurrent kyc simple subject 1453 prepared_transfer 1454 .post("/registration") 1455 .json( 1456 RegistrationRequest { 1457 r#type: TransferType::kyc, 1458 account_pub: acc_pub5, 1459 authorization_pub: auth_pub2, 1460 ..req.clone() 1461 } 1462 .signed(&auth_pair), 1463 ) 1464 .await 1465 .assert_ok_json::<RegistrationResponse>(); 1466 prepared_transfer 1467 .post("/registration") 1468 .json( 1469 RegistrationRequest { 1470 r#type: TransferType::kyc, 1471 account_pub: acc_pub5, 1472 authorization_pub: auth_pub2, 1473 recurrent: true, 1474 ..req.clone() 1475 } 1476 .signed(&auth_pair), 1477 ) 1478 .await 1479 .assert_ok_json::<RegistrationResponse>(); 1480 let pair = Ed25519KeyPair::generate().unwrap(); 1481 prepared_transfer 1482 .post("/registration") 1483 .json( 1484 RegistrationRequest { 1485 r#type: TransferType::kyc, 1486 account_pub: acc_pub5, 1487 authorization_pub: EddsaPublicKey::try_from(pair.public_key().as_ref()).unwrap(), 1488 recurrent: true, 1489 ..req.clone() 1490 } 1491 .signed(&pair), 1492 ) 1493 .await 1494 .assert_ok_json::<RegistrationResponse>(); 1495 wire_gateway 1496 .post("/admin/add-kycauth") 1497 .json(json!({ 1498 "amount": amount, 1499 "account_pub": acc_pub5, 1500 "debit_account": debit_acount, 1501 })) 1502 .await 1503 .assert_ok(); 1504 for _ in 0..2 { 1505 register(&auth_pub2) 1506 .await 1507 .assert_ok_json::<TransferResponse>(); 1508 } 1509 check_in(&[ 1510 Reserve(acc_pub1), 1511 Reserve(acc_pub2), 1512 Reserve(acc_pub3), 1513 Kyc(acc_pub4), 1514 Reserve(acc_pub4), 1515 Pending, 1516 Pending, 1517 Kyc(acc_pub4), 1518 Reserve(auth_pub2), 1519 Bounced, 1520 Bounced, 1521 Reserve(acc_pub5), 1522 Bounced, 1523 Kyc(acc_pub5), 1524 Kyc(acc_pub5), 1525 Pending, 1526 ]) 1527 .await; 1528 1529 // Kyc without using mapping 1530 1531 /* ----- Unregistration ----- */ 1532 let un_req = Unregistration { 1533 timestamp: TalerTimestamp::Timestamp(Timestamp::now()), 1534 authorization_pub: auth_pub2, 1535 authorization_sig: EddsaSignature::ZEROED, 1536 }; 1537 let signed = un_req.clone().signed(&auth_pair); 1538 1539 // Delete 1540 prepared_transfer 1541 .post("/unregistration") 1542 .json(&signed) 1543 .await 1544 .assert_no_content(); 1545 1546 // Check bounce pending on deletion 1547 check_in(&[ 1548 Reserve(acc_pub1), 1549 Reserve(acc_pub2), 1550 Reserve(acc_pub3), 1551 Kyc(acc_pub4), 1552 Reserve(acc_pub4), 1553 Pending, 1554 Pending, 1555 Kyc(acc_pub4), 1556 Reserve(auth_pub2), 1557 Bounced, 1558 Bounced, 1559 Reserve(acc_pub5), 1560 Bounced, 1561 Kyc(acc_pub5), 1562 Kyc(acc_pub5), 1563 Bounced, 1564 ]) 1565 .await; 1566 1567 // Idempotent 1568 prepared_transfer 1569 .post("/unregistration") 1570 .json(&signed) 1571 .await 1572 .assert_error(ErrorCode::BANK_TRANSACTION_NOT_FOUND); 1573 1574 // Bad signature 1575 prepared_transfer 1576 .post("/unregistration") 1577 .json(&un_req) 1578 .await 1579 .assert_error(ErrorCode::BANK_BAD_SIGNATURE); 1580 1581 // Old timestamp 1582 prepared_transfer 1583 .post("/unregistration") 1584 .json( 1585 Unregistration { 1586 timestamp: TalerTimestamp::Timestamp( 1587 Timestamp::now() - SignedDuration::from_mins(10), 1588 ), 1589 ..un_req.clone() 1590 } 1591 .signed(&auth_pair), 1592 ) 1593 .await 1594 .assert_error(ErrorCode::BANK_OLD_TIMESTAMP); 1595 1596 // Never timestamp 1597 prepared_transfer 1598 .post("/unregistration") 1599 .json( 1600 Unregistration { 1601 timestamp: TalerTimestamp::Never, 1602 ..un_req.clone() 1603 } 1604 .signed(&auth_pair), 1605 ) 1606 .await 1607 .assert_error(ErrorCode::BANK_OLD_TIMESTAMP); 1608 1609 // Future timestamp 1610 prepared_transfer 1611 .post("/unregistration") 1612 .json( 1613 Unregistration { 1614 timestamp: TalerTimestamp::Timestamp( 1615 Timestamp::now() + SignedDuration::from_mins(1), 1616 ), 1617 ..un_req.clone() 1618 } 1619 .signed(&auth_pair), 1620 ) 1621 .await 1622 .assert_error(ErrorCode::BANK_OLD_TIMESTAMP); 1623 1624 /* ----- API ----- */ 1625 1626 let history: Vec<_> = wire_gateway 1627 .get("/history/incoming?limit=20") 1628 .await 1629 .assert_ok_json::<IncomingHistory>() 1630 .incoming_transactions 1631 .into_iter() 1632 .map(|tx| { 1633 let (acc_pub, auth_pub, auth_sig) = match tx { 1634 IncomingBankTransaction::Reserve { 1635 reserve_pub, 1636 authorization_pub, 1637 authorization_sig, 1638 .. 1639 } => (reserve_pub, authorization_pub, authorization_sig), 1640 IncomingBankTransaction::Wad { .. } => unreachable!(), 1641 IncomingBankTransaction::Kyc { 1642 account_pub, 1643 authorization_pub, 1644 authorization_sig, 1645 .. 1646 } => (account_pub, authorization_pub, authorization_sig), 1647 }; 1648 assert_eq!(auth_pub.is_some(), auth_sig.is_some()); 1649 (acc_pub, auth_pub) 1650 }) 1651 .collect(); 1652 pretty_assertions::assert_eq!( 1653 history, 1654 [ 1655 (acc_pub1, Some(auth_pub1)), 1656 (acc_pub2, None), 1657 (acc_pub3, Some(auth_pub1)), 1658 (acc_pub4, Some(auth_pub1)), 1659 (acc_pub4, Some(auth_pub1)), 1660 (acc_pub4, None), 1661 (auth_pub2, Some(auth_pub2)), 1662 (acc_pub5, None), 1663 (acc_pub5, None), 1664 (acc_pub5, Some(auth_pub2)), 1665 ] 1666 ) 1667 }