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