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