Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add property tests #5

Merged
merged 9 commits into from
May 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ jobs:

- uses: aiken-lang/[email protected]
with:
version: v1.0.24-alpha
version: v1.0.26-alpha

- run: aiken fmt --check
- run: aiken check -D
- run: aiken build

5 changes: 5 additions & 0 deletions aiken.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ name = "aiken-lang/stdlib"
version = "1.7.0"
source = "github"

[[dependencies]]
name = "aiken-lang/fuzz"
version = "edcfa9d8628133279c16b2b1a9eccbd3c011355d"
source = "github"

[[dependencies]]
name = "aiken-lang/fortuna"
version = "d2e5831f92b1b596bb2cc1ab801509da28650fcc"
Expand Down
1 change: 1 addition & 0 deletions lib/aiken-design-patterns/multi-utxo-indexer.ak
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub fn withdraw(
}
},
)

expect indices: List<(Int, Int)> = indicesData
let (_, _, input_index_count) =
list.foldl(
Expand Down
28 changes: 28 additions & 0 deletions lib/aiken-design-patterns/tests.ak
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
use aiken/bytearray
use aiken/fuzz
use aiken/hash.{blake2b_256}
use aiken/list
use aiken/transaction.{InlineDatum, Output}
use aiken/transaction/credential.{Address, ScriptCredential}
use aiken/transaction/value
use aiken_design_patterns/utils.{
authentic_input_is_reproduced_unchanged, sum_of_squares,
}

// Generate a bytearray with blake2b_256
fn test_224_01() {
bytearray.take(blake2b_256(#"01"), 28)
}

// Generate a bytearray with blake2b_256
fn test_224_02() {
bytearray.take(blake2b_256(#"02"), 28)
}

// Create a UTxO
fn test_utxo_01() {
Output {
address: Address {
Expand All @@ -27,6 +32,7 @@ fn test_utxo_01() {
}
}

// Create a UTxO
fn test_utxo_02() {
Output {
address: Address {
Expand All @@ -39,6 +45,7 @@ fn test_utxo_02() {
}
}

// Test case to ensure authentic UTXO is reproduced unchanged
test authentic_utxo_reproduced() {
authentic_input_is_reproduced_unchanged(
test_224_01(),
Expand All @@ -48,6 +55,7 @@ test authentic_utxo_reproduced() {
)
}

// Test case to ensure unauthentic UTXO is not reproduced unchanged
test unauthentic_utxo_reproduced() fail {
authentic_input_is_reproduced_unchanged(
test_224_01(),
Expand All @@ -57,10 +65,30 @@ test unauthentic_utxo_reproduced() fail {
)
}

// Test case for sum_of_squares function with expected output
test sum_of_squares_test_ok() {
sum_of_squares([100, 20, 3, 4, 5]) == [10450]
}

// Test case for sum_of_squares function with expected failure
test sum_of_squares_test_fail() fail {
sum_of_squares([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) == [1]
}

// Property-based test: Testing the identity property with a single-element list
test prop_sum_of_squares_identity(x: Int via fuzz.int()) {
sum_of_squares([x]) == [x * x]
}

// Property-based test: Testing non-negativity of the sum of squares
test prop_sum_of_squares_non_negative(xs: List<Int> via fuzz.list(fuzz.int())) {
let result = sum_of_squares(xs)
expect Some(head) = list.at(result, 0)
head >= 0
}

// Property-based test: Testing the output always contains exactly one element
test prop_sum_of_squares_length(xs: List<Int> via fuzz.list(fuzz.int())) {
let result = sum_of_squares(xs)
list.length(result) == 1
}
2 changes: 0 additions & 2 deletions lib/aiken-design-patterns/utils.ak
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ pub fn authentic_input_is_reproduced_unchanged(
address: in_addr,
reference_script: None,
} = in_utxo

expect Output {
value: out_val,
datum: out_dat,
Expand All @@ -32,7 +31,6 @@ pub fn authentic_input_is_reproduced_unchanged(
Some(auth_name) -> in_name == auth_name
None -> True
}

and {
in_val == out_val,
in_dat == out_dat,
Expand Down
159 changes: 157 additions & 2 deletions validators/merkelized-validator-example.ak
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
use aiken/dict
use aiken/fuzz
use aiken/hash.{Blake2b_224, Hash}
use aiken/transaction.{ScriptContext, Transaction}
use aiken/transaction/credential.{Script}
use aiken/interval
use aiken/transaction.{
Redeemer, ScriptContext, ScriptPurpose, Transaction, TransactionId,
WithdrawFrom,
}
use aiken/transaction/credential.{Inline, Script, ScriptCredential}
use aiken/transaction/value
use aiken_design_patterns/merkelized_validator.{WithdrawRedeemer} as merkelized_validator
use aiken_design_patterns/utils.{sum_of_squares}

// Definition of a custom validator for spending transactions
validator(stake_validator: Hash<Blake2b_224, Script>) {
fn spend(x: Int, y: Int, ctx: ScriptContext) {
let ScriptContext { transaction: tx, .. } = ctx
Expand All @@ -16,8 +24,155 @@ validator(stake_validator: Hash<Blake2b_224, Script>) {
}
}

// Definition of a custom validator for withdrawal transactions
validator {
fn withdraw(redeemer: WithdrawRedeemer<Int, Int>, ctx: ScriptContext) {
merkelized_validator.withdraw(sum_of_squares, redeemer, ctx)
}
}

// Test case for the spend validator function
test spend_validator() {
let stake_validator: Hash<Blake2b_224, Script> =
#"99999999999999999999999999999999999999999999999999999999"
let redeemer: Data = WithdrawRedeemer { input_args: [2, 4], results: [20] }
let redeemers: dict.Dict<ScriptPurpose, Redeemer> =
dict.new()
|> dict.insert(
WithdrawFrom(Inline(ScriptCredential(stake_validator))),
redeemer,
fn(_, _) -> Ordering { Less },
)
let tx =
Transaction {
inputs: [],
reference_inputs: [],
outputs: [],
fee: value.zero(),
mint: value.zero() |> value.to_minted_value(),
certificates: [],
withdrawals: dict.new(),
validity_range: interval.everything(),
extra_signatories: [],
redeemers,
datums: dict.new(),
id: TransactionId {
hash: #"0000000000000000000000000000000000000000000000000000000000000000",
},
}
let ctx =
ScriptContext {
purpose: WithdrawFrom(
Inline(
ScriptCredential(
#"99999999999999999999999999999999999999999999999999999999",
),
),
),
transaction: tx,
}
spend(stake_validator, 2, 4, ctx)
}

// Test case for the withdraw validator function
test withdraw_validator() {
let redeemer: WithdrawRedeemer<Int, Int> =
WithdrawRedeemer { input_args: [1, 2, 3, 4], results: [30] }
let ctx =
ScriptContext {
purpose: WithdrawFrom(
Inline(
ScriptCredential(
#"99999999999999999999999999999999999999999999999999999999",
),
),
),
transaction: transaction.placeholder(),
}
withdraw(redeemer, ctx)
}

// Definition of a custom data type for withdrawal input
type WithdrawInput {
xs: List<Int>,
bs: ByteArray,
}

// Function to generate random withdrawal input
fn random_withdraw_input() -> Fuzzer<WithdrawInput> {
fuzz.map2(
fuzz.list(fuzz.int()),
fuzz.bytearray_between(56, 56),
fn(xs, bs) { WithdrawInput { xs, bs } },
)
}

// Property test to verify withdraw validator
test prop_withdraw_validator(r via random_withdraw_input()) {
let expected_sum_of_squares = utils.sum_of_squares(r.xs)
let redeemer: WithdrawRedeemer<Int, Int> =
WithdrawRedeemer { input_args: r.xs, results: expected_sum_of_squares }
let ctx =
ScriptContext {
purpose: WithdrawFrom(Inline(ScriptCredential(r.bs))),
transaction: transaction.placeholder(),
}
withdraw(redeemer, ctx)
}

// Definition of a custom data type for spend input
type SpendInput {
x: Int,
y: Int,
bs: ByteArray,
}

// Function to generate random spend input
fn random_spend_input() -> Fuzzer<SpendInput> {
fuzz.map3(
fuzz.int_between(-4, 4),
fuzz.int_between(-4, 4),
fuzz.bytearray_between(56, 56),
fn(x, y, bs) { SpendInput { x, y, bs } },
)
}

// Property test to verify the spend validator
test prop_spend_validator(r via random_spend_input()) {
let xs =
[r.x, r.y]
let expected_sum_of_squares = utils.sum_of_squares(xs)
let stake_validator: Hash<Blake2b_224, Script> = r.bs
let redeemer: Data =
WithdrawRedeemer { input_args: xs, results: expected_sum_of_squares }
let redeemers: dict.Dict<ScriptPurpose, Redeemer> =
dict.new()
|> dict.insert(
WithdrawFrom(Inline(ScriptCredential(stake_validator))),
redeemer,
fn(_, _) -> Ordering { Less },
)
let tx =
Transaction {
inputs: [],
reference_inputs: [],
outputs: [],
fee: value.zero(),
mint: value.zero() |> value.to_minted_value(),
certificates: [],
withdrawals: dict.new(),
validity_range: interval.everything(),
extra_signatories: [],
redeemers,
datums: dict.new(),
id: TransactionId {
hash: #"0000000000000000000000000000000000000000000000000000000000000000",
},
}
let ctx =
ScriptContext {
purpose: WithdrawFrom(Inline(ScriptCredential(r.bs))),
transaction: tx,
}
spend(stake_validator, r.x, r.y, ctx)
}
Loading
Loading