|
| 1 | +use aiken/collection/list |
| 2 | +use aiken/fuzz.{and_then, such_that} |
| 3 | +use aiken/interval |
1 | 4 | use aiken/math |
2 | | -use cardano/assets.{AssetName, Value, add, from_lovelace, zero} |
3 | | -use cardano/transaction.{NoDatum} |
| 5 | +use cardano/address.{Address, Script} |
| 6 | +use cardano/assets.{ |
| 7 | + AssetName, PolicyId, Value, add, from_asset, from_lovelace, zero, |
| 8 | +} |
| 9 | +use cardano/transaction.{ |
| 10 | + InlineDatum, Input, NoDatum, Output, OutputReference, ValidityRange, |
| 11 | +} |
4 | 12 | use payment_subscription/common/cip68 |
5 | | -use payment_subscription/common/types.{Payment, PaymentDatum, Penalty} |
| 13 | +use payment_subscription/common/types.{ |
| 14 | + Installment, Payment, PaymentDatum, PaymentValidatorDatum, Penalty, |
| 15 | + ServiceDatum, |
| 16 | +} |
6 | 17 | use payment_subscription/common/values |
7 | 18 | use payment_subscription/payment_multi_validator/validation.{ |
8 | 19 | payment_tokenname, validate_extend_subscription, validate_initial_subscription, |
9 | 20 | validate_merchant_withdraw, validate_subscriber_withdraw, |
10 | 21 | validate_terminate_subscription, validate_unsubscribe, |
11 | 22 | } |
12 | | -use payment_subscription/tests/tests |
| 23 | +use payment_subscription/tests/tests.{placeholder_input, placeholder_output} |
13 | 24 |
|
14 | 25 | fn setup_policy_ids() { |
15 | 26 | let payment_cs = tests.test_payment_cs() |
@@ -366,6 +377,270 @@ test succeed_initiate_subscription() { |
366 | 377 | ) |
367 | 378 | } |
368 | 379 |
|
| 380 | +type InitSubscriptionTestData { |
| 381 | + payment_policy: PolicyId, |
| 382 | + service_policy: PolicyId, |
| 383 | + account_policy: PolicyId, |
| 384 | + service_ref_input_index: Int, |
| 385 | + subscriber_input_index: Int, |
| 386 | + payment_output_index: Int, |
| 387 | + reference_inputs: List<Input>, |
| 388 | + inputs: List<Input>, |
| 389 | + outputs: List<Output>, |
| 390 | + minted_value: Value, |
| 391 | + validity_range: ValidityRange, |
| 392 | +} |
| 393 | + |
| 394 | +fn gen_output_ref() -> Fuzzer<OutputReference> { |
| 395 | + let transaction_id <- and_then(fuzz.bytearray_fixed(32)) |
| 396 | + let output_index <- and_then(fuzz.int_between(0, 20)) |
| 397 | + fuzz.constant(OutputReference { transaction_id, output_index }) |
| 398 | +} |
| 399 | + |
| 400 | +fn gen_account_input( |
| 401 | + account_policy: PolicyId, |
| 402 | + account_token_seed: OutputReference, |
| 403 | +) -> Fuzzer<Input> { |
| 404 | + let input_ref <- and_then(gen_output_ref()) |
| 405 | + let account_token_name = |
| 406 | + values.unique_token_name( |
| 407 | + account_token_seed.transaction_id, |
| 408 | + account_token_seed.output_index, |
| 409 | + cip68.prefix_222, |
| 410 | + ) |
| 411 | + fuzz.constant( |
| 412 | + Input { |
| 413 | + output_reference: input_ref, |
| 414 | + output: Output { |
| 415 | + address: Address(Script(account_policy), None), |
| 416 | + value: from_asset(account_policy, account_token_name, 1), |
| 417 | + datum: NoDatum, |
| 418 | + reference_script: None, |
| 419 | + }, |
| 420 | + }, |
| 421 | + ) |
| 422 | +} |
| 423 | + |
| 424 | +fn gen_service_input( |
| 425 | + service_policy: PolicyId, |
| 426 | + service_datum: ServiceDatum, |
| 427 | + service_token_name: AssetName, |
| 428 | +) -> Fuzzer<Input> { |
| 429 | + let input_ref <- and_then(gen_output_ref()) |
| 430 | + fuzz.constant( |
| 431 | + Input { |
| 432 | + output_reference: input_ref, |
| 433 | + output: Output { |
| 434 | + address: Address(Script(service_policy), None), |
| 435 | + value: from_asset(service_policy, service_token_name, 1), |
| 436 | + datum: InlineDatum(service_datum), |
| 437 | + reference_script: None, |
| 438 | + }, |
| 439 | + }, |
| 440 | + ) |
| 441 | +} |
| 442 | + |
| 443 | +fn valid_payment_output( |
| 444 | + payment_policy: PolicyId, |
| 445 | + payment_datum: PaymentValidatorDatum, |
| 446 | + service_datum: ServiceDatum, |
| 447 | +) -> Output { |
| 448 | + let value = |
| 449 | + add( |
| 450 | + add(from_lovelace(2_000_000), payment_policy, payment_tokenname, 1), |
| 451 | + service_datum.service_fee_policyid, |
| 452 | + service_datum.service_fee_assetname, |
| 453 | + service_datum.num_intervals * service_datum.service_fee, |
| 454 | + ) |
| 455 | + Output { |
| 456 | + address: Address(Script(payment_policy), None), |
| 457 | + value, |
| 458 | + datum: InlineDatum(payment_datum), |
| 459 | + reference_script: None, |
| 460 | + } |
| 461 | +} |
| 462 | + |
| 463 | +fn gen_list_single_index(default: a, indexed: a) -> Fuzzer<(Int, List<a>)> { |
| 464 | + let count <- and_then(fuzz.int_between(1, 20)) |
| 465 | + let index <- and_then(fuzz.int_between(0, count - 1)) |
| 466 | + fuzz.constant( |
| 467 | + ( |
| 468 | + index, |
| 469 | + list.map( |
| 470 | + list.range(0, count - 1), |
| 471 | + fn(i) { |
| 472 | + if i == index { |
| 473 | + indexed |
| 474 | + } else { |
| 475 | + default |
| 476 | + } |
| 477 | + }, |
| 478 | + ), |
| 479 | + ), |
| 480 | + ) |
| 481 | +} |
| 482 | + |
| 483 | +fn gen_service_reference_token_name() -> Fuzzer<AssetName> { |
| 484 | + fuzz.bytearray_between(1, 32) |
| 485 | +} |
| 486 | + |
| 487 | +fn gen_valid_service_datum() -> Fuzzer<ServiceDatum> { |
| 488 | + let service_fee_policyid <- and_then(fuzz.bytearray_fixed(28)) |
| 489 | + let service_fee_assetname <- and_then(fuzz.bytearray_between(1, 32)) |
| 490 | + let service_fee <- and_then(fuzz.int_between(1, 100_000_000_000_000)) |
| 491 | + let penalty_fee_policyid <- and_then(fuzz.bytearray_fixed(28)) |
| 492 | + let penalty_fee_assetname <- and_then(fuzz.bytearray_between(1, 32)) |
| 493 | + let penalty_fee <- and_then(fuzz.int_between(1, 100_000_000_000_000)) |
| 494 | + let interval_length <- and_then(fuzz.int_between(1, 100_000_000_000_000)) |
| 495 | + let num_intervals <- and_then(fuzz.int_between(1, 1_000)) |
| 496 | + fuzz.constant( |
| 497 | + ServiceDatum { |
| 498 | + service_fee_policyid, |
| 499 | + service_fee_assetname, |
| 500 | + service_fee, |
| 501 | + penalty_fee_policyid, |
| 502 | + penalty_fee_assetname, |
| 503 | + penalty_fee, |
| 504 | + interval_length, |
| 505 | + num_intervals, |
| 506 | + is_active: True, |
| 507 | + }, |
| 508 | + ) |
| 509 | +} |
| 510 | + |
| 511 | +fn gen_valid_payment_datum( |
| 512 | + current_time: Int, |
| 513 | + service_datum: ServiceDatum, |
| 514 | + service_reference_tokenname: AssetName, |
| 515 | + subscriber_reference_tokenname: AssetName, |
| 516 | +) -> Fuzzer<PaymentDatum> { |
| 517 | + let subscription_start <- and_then(fuzz.int_at_least(current_time)) |
| 518 | + let subscription_end = |
| 519 | + subscription_start + service_datum.interval_length * service_datum.num_intervals |
| 520 | + let installments = |
| 521 | + list.map( |
| 522 | + list.range(1, service_datum.num_intervals), |
| 523 | + fn(i) { |
| 524 | + Installment { |
| 525 | + claimable_at: subscription_start + i * service_datum.interval_length, |
| 526 | + claimable_amount: service_datum.service_fee, |
| 527 | + } |
| 528 | + }, |
| 529 | + ) |
| 530 | + fuzz.constant( |
| 531 | + PaymentDatum { |
| 532 | + service_reference_tokenname, |
| 533 | + subscriber_reference_tokenname, |
| 534 | + subscription_start, |
| 535 | + subscription_end, |
| 536 | + original_subscription_end: subscription_end, |
| 537 | + installments, |
| 538 | + }, |
| 539 | + ) |
| 540 | +} |
| 541 | + |
| 542 | +fn policies_are_different( |
| 543 | + service_datum: ServiceDatum, |
| 544 | + policies_not_allowed: List<PolicyId>, |
| 545 | +) -> Bool { |
| 546 | + and { |
| 547 | + !list.any( |
| 548 | + policies_not_allowed, |
| 549 | + fn(p) { service_datum.service_fee_policyid == p }, |
| 550 | + ), |
| 551 | + !list.any( |
| 552 | + policies_not_allowed, |
| 553 | + fn(p) { service_datum.penalty_fee_policyid == p }, |
| 554 | + ), |
| 555 | + } |
| 556 | +} |
| 557 | + |
| 558 | +fn gen_init_subscription_success() -> Fuzzer<InitSubscriptionTestData> { |
| 559 | + let (payment_policy, account_policy, service_policy) = setup_policy_ids() |
| 560 | + let account_token_seed <- and_then(gen_output_ref()) |
| 561 | + let subscriber_reference_tokenname = |
| 562 | + values.unique_token_name( |
| 563 | + account_token_seed.transaction_id, |
| 564 | + account_token_seed.output_index, |
| 565 | + cip68.prefix_100, |
| 566 | + ) |
| 567 | + let account_ref <- |
| 568 | + and_then(gen_account_input(account_policy, account_token_seed)) |
| 569 | + let (subscriber_input_index, inputs) <- |
| 570 | + and_then(gen_list_single_index(placeholder_input, account_ref)) |
| 571 | + let service_datum <- |
| 572 | + and_then( |
| 573 | + such_that( |
| 574 | + gen_valid_service_datum(), |
| 575 | + fn(s) { |
| 576 | + policies_are_different( |
| 577 | + s, |
| 578 | + [payment_policy, account_policy, service_policy], |
| 579 | + ) |
| 580 | + }, |
| 581 | + ), |
| 582 | + ) |
| 583 | + let service_reference_tokenname <- |
| 584 | + and_then(gen_service_reference_token_name()) |
| 585 | + let service_input <- |
| 586 | + and_then( |
| 587 | + gen_service_input( |
| 588 | + service_policy, |
| 589 | + service_datum, |
| 590 | + service_reference_tokenname, |
| 591 | + ), |
| 592 | + ) |
| 593 | + let (service_ref_input_index, reference_inputs) <- |
| 594 | + and_then(gen_list_single_index(placeholder_input, service_input)) |
| 595 | + let current_time <- and_then(fuzz.int()) |
| 596 | + let payment_datum <- |
| 597 | + and_then( |
| 598 | + gen_valid_payment_datum( |
| 599 | + current_time, |
| 600 | + service_datum, |
| 601 | + service_reference_tokenname, |
| 602 | + subscriber_reference_tokenname, |
| 603 | + ), |
| 604 | + ) |
| 605 | + let payment_output = |
| 606 | + valid_payment_output(payment_policy, Payment(payment_datum), service_datum) |
| 607 | + let (payment_output_index, outputs) <- |
| 608 | + and_then(gen_list_single_index(placeholder_output, payment_output)) |
| 609 | + fuzz.constant( |
| 610 | + InitSubscriptionTestData { |
| 611 | + payment_policy, |
| 612 | + service_policy, |
| 613 | + account_policy, |
| 614 | + service_ref_input_index, |
| 615 | + subscriber_input_index, |
| 616 | + payment_output_index, |
| 617 | + reference_inputs, |
| 618 | + inputs, |
| 619 | + outputs, |
| 620 | + minted_value: from_asset(payment_policy, payment_tokenname, 1), |
| 621 | + validity_range: interval.entirely_before(payment_datum.subscription_start), |
| 622 | + }, |
| 623 | + ) |
| 624 | +} |
| 625 | + |
| 626 | +test prop_successful_initiate_subscription( |
| 627 | + test_values via gen_init_subscription_success(), |
| 628 | +) { |
| 629 | + validate_initial_subscription( |
| 630 | + test_values.payment_policy, |
| 631 | + test_values.service_policy, |
| 632 | + test_values.account_policy, |
| 633 | + test_values.service_ref_input_index, |
| 634 | + test_values.subscriber_input_index, |
| 635 | + test_values.payment_output_index, |
| 636 | + test_values.reference_inputs, |
| 637 | + test_values.inputs, |
| 638 | + test_values.outputs, |
| 639 | + test_values.minted_value, |
| 640 | + test_values.validity_range, |
| 641 | + ) |
| 642 | +} |
| 643 | + |
369 | 644 | test succeed_terminate_subscription() { |
370 | 645 | let payment_cs = tests.test_payment_cs() |
371 | 646 |
|
|
0 commit comments