Skip to content

Commit 230ea95

Browse files
authored
Merge pull request #12 from Anastasia-Labs/payment-property-based-tests
Payment property based tests
2 parents d77d4b4 + 7735a7e commit 230ea95

2 files changed

Lines changed: 303 additions & 6 deletions

File tree

lib/payment-subscription/tests/payment-multi-validator.ak

Lines changed: 279 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1+
use aiken/collection/list
2+
use aiken/fuzz.{and_then, such_that}
3+
use aiken/interval
14
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+
}
412
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+
}
617
use payment_subscription/common/values
718
use payment_subscription/payment_multi_validator/validation.{
819
payment_tokenname, validate_extend_subscription, validate_initial_subscription,
920
validate_merchant_withdraw, validate_subscriber_withdraw,
1021
validate_terminate_subscription, validate_unsubscribe,
1122
}
12-
use payment_subscription/tests/tests
23+
use payment_subscription/tests/tests.{placeholder_input, placeholder_output}
1324

1425
fn setup_policy_ids() {
1526
let payment_cs = tests.test_payment_cs()
@@ -366,6 +377,270 @@ test succeed_initiate_subscription() {
366377
)
367378
}
368379

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+
369644
test succeed_terminate_subscription() {
370645
let payment_cs = tests.test_payment_cs()
371646

lib/payment-subscription/tests/tests.ak

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,34 @@ use aiken/crypto.{blake2b_256}
33
use aiken/interval.{Finite, Interval, IntervalBound}
44
use aiken/primitive/bytearray
55
use cardano/address.{Address, Script, VerificationKey}
6-
use cardano/assets.{ada_asset_name, ada_policy_id, add, from_lovelace}
7-
use cardano/transaction.{InlineDatum, Input, Output, OutputReference}
6+
use cardano/assets.{ada_asset_name, ada_policy_id, add, from_lovelace, zero}
7+
use cardano/transaction.{InlineDatum, Input, NoDatum, Output, OutputReference}
88
use payment_subscription/common/types.{
99
AccountDatum, Installment, PaymentDatum, PenaltyDatum, ServiceDatum,
1010
}
1111

12+
pub const placeholder_output =
13+
Output {
14+
address: Address {
15+
payment_credential: VerificationKey(
16+
#"00000000000000000000000000000000000000000000000000000000",
17+
),
18+
stake_credential: None,
19+
},
20+
value: zero,
21+
datum: NoDatum,
22+
reference_script: None,
23+
}
24+
25+
pub const placeholder_input =
26+
Input {
27+
output_reference: OutputReference {
28+
transaction_id: #"0000000000000000000000000000000000000000000000000000000000000000",
29+
output_index: 0,
30+
},
31+
output: placeholder_output,
32+
}
33+
1234
// Generate a bytearray with blake2b_256
1335
pub fn test_224_01() {
1436
bytearray.take(blake2b_256(#"01"), 28)

0 commit comments

Comments
 (0)