diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f5ba2d8..bed4e67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,7 +22,7 @@ env: jobs: simplex-tests: runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 60 permissions: contents: read strategy: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8957b4e..eaf6170 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,5 @@ # Changelog -## [0.0.1] - -The initial release! -- Сhecked arithmetic operations for `u8`, `u16`, `u32`, `u64`, and `u128`; -- `OP_RETURN` detection utilities. -- Implementation of `and`, `or`, `not`, and `xor` binary operators. -- Basic numeric assertions. -- Equality and conversion operators for `secp256k1` points. - ## [Unreleased] - Added `mul_div` functions. @@ -16,3 +7,12 @@ The initial release! - Refactored test structure. - Removed `safe_` prefixes from math functions, so the default versions are overflow-checked. - Added functions for retrieving and asserting `AssetId` and `Amount`. + +## [0.0.1] + +Initial release! +- Сhecked arithmetic operations for `u8`, `u16`, `u32`, `u64`, and `u128`. +- `OP_RETURN` detection utilities. +- Implementation of `and`, `or`, `not`, and `xor` binary operators. +- Basic numeric assertions. +- Equality and conversion operators for `secp256k1` points. diff --git a/docs/stdlib.json b/docs/stdlib.json index 7a88ddc..ad2e2cc 100644 --- a/docs/stdlib.json +++ b/docs/stdlib.json @@ -104,6 +104,174 @@ "output_type": "()", "description": "Assert that two `bool` values are equal.\n\n## Panics\nThe assertion fails." }, + { + "simplicityhl_name": "assert_explicit_amount", + "section": "Amount asserts", + "input_type": "u32, bool, u64", + "output_type": "()", + "description": "Asserts that the explicit amount at the specified input or output index equals the expected u64 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_amount", + "section": "Amount asserts", + "input_type": "u32, bool, Confidential1", + "output_type": "()", + "description": "Asserts that the confidential amount at the specified input or output index equals the expected Confidential1 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_input_amount", + "section": "Amount asserts", + "input_type": "u32, u64", + "output_type": "()", + "description": "Asserts that the explicit amount at the specified input index equals the expected u64 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_input_amount", + "section": "Amount asserts", + "input_type": "u32, Confidential1", + "output_type": "()", + "description": "Asserts that the confidential amount at the specified input index equals the expected Confidential1 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_output_amount", + "section": "Amount asserts", + "input_type": "u32, u64", + "output_type": "()", + "description": "Asserts that the explicit amount at the specified output index equals the expected u64 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_output_amount", + "section": "Amount asserts", + "input_type": "u32, Confidential1", + "output_type": "()", + "description": "Asserts that the confidential amount at the specified output index equals the expected Confidential1 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_current_amount", + "section": "Amount asserts", + "input_type": "u64", + "output_type": "()", + "description": "Asserts that the explicit input amount at the current_index equals the expected u64 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_current_amount", + "section": "Amount asserts", + "input_type": "Confidential1", + "output_type": "()", + "description": "Asserts that the confidential input amount at the current_index equals the expected Confidential1 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_asset_id_with_amount", + "section": "AssetId and Amount asserts", + "input_type": "u32, bool, u256, u64", + "output_type": "()", + "description": "Asserts that explicit asset_id and amount at the specified input or output index equal the expected u256 and u64 values.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_asset_id_with_amount", + "section": "AssetId and Amount asserts", + "input_type": "u32, bool, Confidential1, Confidential1", + "output_type": "()", + "description": "Asserts that confidential asset_id and amount at the specified input or output index equal the expected Confidential1 values.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_input_asset_id_with_amount", + "section": "AssetId and Amount asserts", + "input_type": "u32, u256, u64", + "output_type": "()", + "description": "Asserts that explicit asset_id and amount at the specified input index equal the expected u256 and u64 values.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_input_asset_id_with_amount", + "section": "AssetId and Amount asserts", + "input_type": "u32, Confidential1, Confidential1", + "output_type": "()", + "description": "Asserts that confidential asset_id and amount at the specified input index equal the expected Confidential1 values.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_output_asset_id_with_amount", + "section": "AssetId and Amount asserts", + "input_type": "u32, u256, u64", + "output_type": "()", + "description": "Asserts that explicit asset_id and amount at the specified output index equal the expected u256 and u64 values.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_output_asset_id_with_amount", + "section": "AssetId and Amount asserts", + "input_type": "u32, Confidential1, Confidential1", + "output_type": "()", + "description": "Asserts that confidential asset_id and amount at the specified output index equal the expected Confidential1 values.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_current_asset_id_with_amount", + "section": "AssetId and Amount asserts", + "input_type": "u256, u64", + "output_type": "()", + "description": "Asserts that explicit input asset_id and amount at the current_index equal the expected u256 and u64 values.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_current_asset_id_with_amount", + "section": "AssetId and Amount asserts", + "input_type": "Confidential1, Confidential1", + "output_type": "()", + "description": "Asserts that confidential input asset_id and amount at the current_index equal the expected Confidential1 values.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_asset_id", + "section": "AssetId asserts", + "input_type": "u32, bool, u256", + "output_type": "()", + "description": "Asserts that the explicit asset_id at the specified input or output index equals the expected u256 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_asset_id", + "section": "AssetId asserts", + "input_type": "u32, bool, Confidential1", + "output_type": "()", + "description": "Asserts that the confidential asset_id at the specified input or output index equals the expected Confidential1 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_input_asset_id", + "section": "AssetId asserts", + "input_type": "u32, u256", + "output_type": "()", + "description": "Asserts that the explicit asset_id at the specified input index equals the expected u256 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_input_asset_id", + "section": "AssetId asserts", + "input_type": "u32, Confidential1", + "output_type": "()", + "description": "Asserts that the confidential asset_id at the specified input index equals the expected Confidential1 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_output_asset_id", + "section": "AssetId asserts", + "input_type": "u32, u256", + "output_type": "()", + "description": "Asserts that the explicit asset_id at the specified output index equals the expected u256 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_output_asset_id", + "section": "AssetId asserts", + "input_type": "u32, Confidential1", + "output_type": "()", + "description": "Asserts that the confidential asset_id at the specified output index equals the expected Confidential1 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_explicit_current_asset_id", + "section": "AssetId asserts", + "input_type": "u256", + "output_type": "()", + "description": "Asserts that the explicit input asset_id at the current_index equals the expected u256 value.\n\n## Panics\nThe assertion fails." + }, + { + "simplicityhl_name": "assert_confidential_current_asset_id", + "section": "AssetId asserts", + "input_type": "Confidential1", + "output_type": "()", + "description": "Asserts that the confidential input asset_id at the current_index equals the expected Confidential1 value.\n\n## Panics\nThe assertion fails." + }, { "simplicityhl_name": "get_explicit_amount", "section": "Amount getters", @@ -1410,7 +1578,7 @@ "simplicityhl_name": "div_mod_256_64", "section": "`u256` arithmetic", "input_type": "u256, u64", - "output_type": "(u256, u64)", + "output_type": "()", "description": "Divide a `u256` value by a `u64` value, returning the `u256` quotient and the `u64` remainder.\n\n## Panics\nPanics if the divisor is zero." }, { diff --git a/simf/lib/utxo/asserts/amount.simf b/simf/lib/utxo/asserts/amount.simf new file mode 100644 index 0000000..ffc390b --- /dev/null +++ b/simf/lib/utxo/asserts/amount.simf @@ -0,0 +1,81 @@ +use crate::lib::utxo::getters::amount::{ + get_explicit_amount, + get_confidential_amount, + get_explicit_input_amount, + get_confidential_input_amount, + get_explicit_output_amount, + get_confidential_output_amount, + get_explicit_current_amount, + get_confidential_current_amount +}; +use crate::lib::asserts::{assert_eq_1, assert_eq_64, assert_eq_256}; + +/// Asserts that the explicit amount at the specified input or output equals the expected u64 value +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn assert_explicit_amount(index: u32, is_input_index: bool, expected: u64) { + let amount: u64 = get_explicit_amount(index, is_input_index); + + assert_eq_64(amount, expected); +} + +/// Asserts that the confidential amount at the specified input or output equals the expected Confidential1 value +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn assert_confidential_amount(index: u32, is_input_index: bool, expected: Confidential1) { + let (parity_bit, amount): Confidential1 = get_confidential_amount(index, is_input_index); + let (expected_parity_bit, expected_amount): Confidential1 = expected; + + assert_eq_1(parity_bit, expected_parity_bit); + assert_eq_256(amount, expected_amount); +} + +/// Asserts that the explicit amount at the specified input equals the expected u64 value +pub fn assert_explicit_input_amount(index: u32, expected: u64) { + let amount: u64 = get_explicit_input_amount(index); + + assert_eq_64(amount, expected); +} + +/// Asserts that the confidential amount at the specified input equals the expected Confidential1 value +pub fn assert_confidential_input_amount(index: u32, expected: Confidential1) { + let (parity_bit, amount): Confidential1 = get_confidential_input_amount(index); + let (expected_parity_bit, expected_amount): Confidential1 = expected; + + assert_eq_1(parity_bit, expected_parity_bit); + assert_eq_256(amount, expected_amount); +} + +/// Asserts that the explicit amount at the specified output equals the expected u64 value +pub fn assert_explicit_output_amount(index: u32, expected: u64) { + let amount: u64 = get_explicit_output_amount(index); + + assert_eq_64(amount, expected); +} + +/// Asserts that the confidential amount at the specified output equals the expected Confidential1 value +pub fn assert_confidential_output_amount(index: u32, expected: Confidential1) { + let (parity_bit, amount): Confidential1 = get_confidential_output_amount(index); + let (expected_parity_bit, expected_amount): Confidential1 = expected; + + assert_eq_1(parity_bit, expected_parity_bit); + assert_eq_256(amount, expected_amount); +} + +/// Asserts that the explicit input amount at the current_index equals the expected u64 value +pub fn assert_explicit_current_amount(expected: u64) { + let amount: u64 = get_explicit_current_amount(); + + assert_eq_64(amount, expected); +} + +/// Asserts that the confidential input amount at the current_index equals the expected Confidential1 value +pub fn assert_confidential_current_amount(expected: Confidential1) { + let (parity_bit, amount): Confidential1 = get_confidential_current_amount(); + let (expected_parity_bit, expected_amount): Confidential1 = expected; + + assert_eq_1(parity_bit, expected_parity_bit); + assert_eq_256(amount, expected_amount); +} diff --git a/simf/lib/utxo/asserts/asset_id.simf b/simf/lib/utxo/asserts/asset_id.simf new file mode 100644 index 0000000..1e1d702 --- /dev/null +++ b/simf/lib/utxo/asserts/asset_id.simf @@ -0,0 +1,81 @@ +use crate::lib::utxo::getters::asset_id::{ + get_explicit_asset_id, + get_confidential_asset_id, + get_explicit_input_asset_id, + get_confidential_input_asset_id, + get_explicit_output_asset_id, + get_confidential_output_asset_id, + get_explicit_current_asset_id, + get_confidential_current_asset_id +}; +use crate::lib::asserts::{assert_eq_1, assert_eq_256}; + +/// Asserts that the explicit asset_id at the specified input or output equals the expected u256 value +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn assert_explicit_asset_id(index: u32, is_input_index: bool, expected: u256) { + let asset_id: u256 = get_explicit_asset_id(index, is_input_index); + + assert_eq_256(asset_id, expected); +} + +/// Asserts that the confidential asset_id at the specified input or output equals the expected Confidential1 value +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn assert_confidential_asset_id(index: u32, is_input_index: bool, expected: Confidential1) { + let (parity_bit, asset_id): Confidential1 = get_confidential_asset_id(index, is_input_index); + let (expected_parity_bit, expected_asset_id): Confidential1 = expected; + + assert_eq_1(parity_bit, expected_parity_bit); + assert_eq_256(asset_id, expected_asset_id); +} + +/// Asserts that the explicit asset_id at the specified input equals the expected u256 value +pub fn assert_explicit_input_asset_id(index: u32, expected: u256) { + let asset_id: u256 = get_explicit_input_asset_id(index); + + assert_eq_256(asset_id, expected); +} + +/// Asserts that the confidential asset_id at the specified input equals the expected Confidential1 value +pub fn assert_confidential_input_asset_id(index: u32, expected: Confidential1) { + let (parity_bit, asset_id): Confidential1 = get_confidential_input_asset_id(index); + let (expected_parity_bit, expected_asset_id): Confidential1 = expected; + + assert_eq_1(parity_bit, expected_parity_bit); + assert_eq_256(asset_id, expected_asset_id); +} + +/// Asserts that the explicit asset_id at the specified output equals the expected u256 value +pub fn assert_explicit_output_asset_id(index: u32, expected: u256) { + let asset_id: u256 = get_explicit_output_asset_id(index); + + assert_eq_256(asset_id, expected); +} + +/// Asserts that the confidential asset_id at the specified output equals the expected Confidential1 value +pub fn assert_confidential_output_asset_id(index: u32, expected: Confidential1) { + let (parity_bit, asset_id): Confidential1 = get_confidential_output_asset_id(index); + let (expected_parity_bit, expected_asset_id): Confidential1 = expected; + + assert_eq_1(parity_bit, expected_parity_bit); + assert_eq_256(asset_id, expected_asset_id); +} + +/// Asserts that the explicit input asset_id at the current_index equals the expected u256 value +pub fn assert_explicit_current_asset_id(expected: u256) { + let asset_id: u256 = get_explicit_current_asset_id(); + + assert_eq_256(asset_id, expected); +} + +/// Asserts that the confidential input asset_id at the current_index equals the expected Confidential1 value +pub fn assert_confidential_current_asset_id(expected: Confidential1) { + let (parity_bit, asset_id): Confidential1 = get_confidential_current_asset_id(); + let (expected_parity_bit, expected_asset_id): Confidential1 = expected; + + assert_eq_1(parity_bit, expected_parity_bit); + assert_eq_256(asset_id, expected_asset_id); +} diff --git a/simf/lib/utxo/asserts/asset_id_with_amount.simf b/simf/lib/utxo/asserts/asset_id_with_amount.simf new file mode 100644 index 0000000..f577748 --- /dev/null +++ b/simf/lib/utxo/asserts/asset_id_with_amount.simf @@ -0,0 +1,165 @@ +use crate::lib::utxo::getters::asset_id_with_amount::{ + get_explicit_asset_id_with_amount, + get_confidential_asset_id_with_amount, + get_explicit_input_asset_id_with_amount, + get_confidential_input_asset_id_with_amount, + get_explicit_output_asset_id_with_amount, + get_confidential_output_asset_id_with_amount, + get_explicit_current_asset_id_with_amount, + get_confidential_current_asset_id_with_amount +}; +use crate::lib::asserts::{assert_eq_1, assert_eq_64, assert_eq_256}; + +/// Asserts that the explicit asset_id and amount at the specified input or output equal the expected u256 and u64 values +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn assert_explicit_asset_id_with_amount( + index: u32, + is_input_index: bool, + expected_asset_id: u256, + expected_amount: u64 +) { + let (asset_id, amount): (u256, u64) = get_explicit_asset_id_with_amount(index, is_input_index); + + assert_eq_256(asset_id, expected_asset_id); + assert_eq_64(amount, expected_amount); +} + +/// Asserts that the confidential asset_id and amount at the specified input or output equal the expected Confidential1 values +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn assert_confidential_asset_id_with_amount( + index: u32, + is_input_index: bool, + expected_asset_id: Confidential1, + expected_amount: Confidential1 +) { + let ( + asset_id, + amount + ): (Confidential1, Confidential1) = get_confidential_asset_id_with_amount( + index, + is_input_index + ); + + /// asset_id + let (asset_id_parity_bit, asset_id): Confidential1 = asset_id; + let (expected_asset_id_parity_bit, expected_asset_id): Confidential1 = expected_asset_id; + // amount + let (amount_parity_bit, amount): Confidential1 = amount; + let (expected_amount_parity_bit, expected_amount): Confidential1 = expected_amount; + // asset_id + assert_eq_1(asset_id_parity_bit, expected_asset_id_parity_bit); + assert_eq_256(asset_id, expected_asset_id); + // amount + assert_eq_1(amount_parity_bit, expected_amount_parity_bit); + assert_eq_256(amount, expected_amount); +} + +/// Asserts that the explicit asset_id and amount at the specified input equal the expected u256 and u64 values +pub fn assert_explicit_input_asset_id_with_amount( + index: u32, + expected_asset_id: u256, + expected_amount: u64 +) { + let (asset_id, amount): (u256, u64) = get_explicit_input_asset_id_with_amount(index); + + assert_eq_256(asset_id, expected_asset_id); + assert_eq_64(amount, expected_amount); +} + +/// Asserts that the confidential asset_id and amount at the specified input equal the expected Confidential1 values +pub fn assert_confidential_input_asset_id_with_amount( + index: u32, + expected_asset_id: Confidential1, + expected_amount: Confidential1 +) { + let ( + asset_id, + amount + ): (Confidential1, Confidential1) = get_confidential_input_asset_id_with_amount(index); + + // asset_id + let (asset_id_parity_bit, asset_id): Confidential1 = asset_id; + let (expected_asset_id_parity_bit, expected_asset_id): Confidential1 = expected_asset_id; + // amount + let (amount_parity_bit, amount): Confidential1 = amount; + let (expected_amount_parity_bit, expected_amount): Confidential1 = expected_amount; + // asset_id + assert_eq_1(asset_id_parity_bit, expected_asset_id_parity_bit); + assert_eq_256(asset_id, expected_asset_id); + // amount + assert_eq_1(amount_parity_bit, expected_amount_parity_bit); + assert_eq_256(amount, expected_amount); +} + +/// Asserts that the explicit asset_id and amount at the specified output equal the expected u256 and u64 values +pub fn assert_explicit_output_asset_id_with_amount( + index: u32, + expected_asset_id: u256, + expected_amount: u64 +) { + let (asset_id, amount): (u256, u64) = get_explicit_output_asset_id_with_amount(index); + + assert_eq_256(asset_id, expected_asset_id); + assert_eq_64(amount, expected_amount); +} + +/// Asserts that the confidential asset_id and amount at the specified output equal the expected Confidential1 values +pub fn assert_confidential_output_asset_id_with_amount( + index: u32, + expected_asset_id: Confidential1, + expected_amount: Confidential1 +) { + let ( + asset_id, + amount + ): (Confidential1, Confidential1) = get_confidential_output_asset_id_with_amount(index); + + // asset_id + let (asset_id_parity_bit, asset_id): Confidential1 = asset_id; + let (expected_asset_id_parity_bit, expected_asset_id): Confidential1 = expected_asset_id; + // amount + let (amount_parity_bit, amount): Confidential1 = amount; + let (expected_amount_parity_bit, expected_amount): Confidential1 = expected_amount; + // asset_id + assert_eq_1(asset_id_parity_bit, expected_asset_id_parity_bit); + assert_eq_256(asset_id, expected_asset_id); + // amount + assert_eq_1(amount_parity_bit, expected_amount_parity_bit); + assert_eq_256(amount, expected_amount); +} + +/// Asserts that the explicit input asset_id and amount at the current_index equal the expected u256 and u64 values +pub fn assert_explicit_current_asset_id_with_amount(expected_asset_id: u256, expected_amount: u64) { + let (asset_id, amount): (u256, u64) = get_explicit_current_asset_id_with_amount(); + + assert_eq_256(asset_id, expected_asset_id); + assert_eq_64(amount, expected_amount); +} + +/// Asserts that the confidential input asset_id and amount at the current_index equal the expected Confidential1 values +pub fn assert_confidential_current_asset_id_with_amount( + expected_asset_id: Confidential1, + expected_amount: Confidential1 +) { + let ( + asset_id, + amount + ): (Confidential1, Confidential1) = get_confidential_current_asset_id_with_amount(); + + // asset_id + let (asset_id_parity_bit, asset_id): Confidential1 = asset_id; + let (expected_asset_id_parity_bit, expected_asset_id): Confidential1 = expected_asset_id; + // amount + let (amount_parity_bit, amount): Confidential1 = amount; + let (expected_amount_parity_bit, expected_amount): Confidential1 = expected_amount; + // asset_id + assert_eq_1(asset_id_parity_bit, expected_asset_id_parity_bit); + assert_eq_256(asset_id, expected_asset_id); + // amount + assert_eq_1(amount_parity_bit, expected_amount_parity_bit); + assert_eq_256(amount, expected_amount); +} diff --git a/simf/tests/utxo/asserts/amount.simf b/simf/tests/utxo/asserts/amount.simf new file mode 100644 index 0000000..c9532e8 --- /dev/null +++ b/simf/tests/utxo/asserts/amount.simf @@ -0,0 +1,81 @@ +use crate::lib::utxo::asserts::amount::{ + assert_explicit_amount, + assert_confidential_amount, + assert_explicit_input_amount, + assert_confidential_input_amount, + assert_explicit_output_amount, + assert_confidential_output_amount, + assert_explicit_current_amount, + assert_confidential_current_amount +}; + +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let index: u32 = witness::INDEX; + let is_input_index: bool = witness::IS_INPUT_INDEX; + + let expected_amount: u64 = witness::EXPECTED; + let expected_parity: u1 = witness::EXPECTED_PARITY; + let expected_confidential_amount: u256 = witness::EXPECTED_CONF; + + let expected_confidential: Confidential1 = (expected_parity, expected_confidential_amount); + + match is_selected(0, fn_idx) { + true => { + assert_explicit_amount(index, is_input_index, expected_amount); + }, + false => (), + }; + + match is_selected(1, fn_idx) { + true => { + assert_confidential_amount(index, is_input_index, expected_confidential); + }, + false => (), + }; + + match is_selected(2, fn_idx) { + true => { + assert_explicit_input_amount(index, expected_amount); + }, + false => (), + }; + + match is_selected(3, fn_idx) { + true => { + assert_confidential_input_amount(index, expected_confidential); + }, + false => (), + }; + + match is_selected(4, fn_idx) { + true => { + assert_explicit_output_amount(index, expected_amount); + }, + false => (), + }; + + match is_selected(5, fn_idx) { + true => { + assert_confidential_output_amount(index, expected_confidential); + }, + false => (), + }; + + match is_selected(6, fn_idx) { + true => { + assert_explicit_current_amount(expected_amount); + }, + false => (), + }; + + match is_selected(7, fn_idx) { + true => { + assert_confidential_current_amount(expected_confidential); + }, + false => (), + }; +} diff --git a/simf/tests/utxo/asserts/asset_id.simf b/simf/tests/utxo/asserts/asset_id.simf new file mode 100644 index 0000000..f770118 --- /dev/null +++ b/simf/tests/utxo/asserts/asset_id.simf @@ -0,0 +1,81 @@ +use crate::lib::utxo::asserts::asset_id::{ + assert_explicit_asset_id, + assert_confidential_asset_id, + assert_explicit_input_asset_id, + assert_confidential_input_asset_id, + assert_explicit_output_asset_id, + assert_confidential_output_asset_id, + assert_explicit_current_asset_id, + assert_confidential_current_asset_id +}; + +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let index: u32 = witness::INDEX; + let is_input_index: bool = witness::IS_INPUT_INDEX; + + let expected_asset_id: u256 = witness::EXPECTED; + let expected_parity: u1 = witness::EXPECTED_PARITY; + let expected_confidential_asset_id: u256 = witness::EXPECTED_CONF; + + let expected_confidential: Confidential1 = (expected_parity, expected_confidential_asset_id); + + match is_selected(0, fn_idx) { + true => { + assert_explicit_asset_id(index, is_input_index, expected_asset_id); + }, + false => (), + }; + + match is_selected(1, fn_idx) { + true => { + assert_confidential_asset_id(index, is_input_index, expected_confidential); + }, + false => (), + }; + + match is_selected(2, fn_idx) { + true => { + assert_explicit_input_asset_id(index, expected_asset_id); + }, + false => (), + }; + + match is_selected(3, fn_idx) { + true => { + assert_confidential_input_asset_id(index, expected_confidential); + }, + false => (), + }; + + match is_selected(4, fn_idx) { + true => { + assert_explicit_output_asset_id(index, expected_asset_id); + }, + false => (), + }; + + match is_selected(5, fn_idx) { + true => { + assert_confidential_output_asset_id(index, expected_confidential); + }, + false => (), + }; + + match is_selected(6, fn_idx) { + true => { + assert_explicit_current_asset_id(expected_asset_id); + }, + false => (), + }; + + match is_selected(7, fn_idx) { + true => { + assert_confidential_current_asset_id(expected_confidential); + }, + false => (), + }; +} diff --git a/simf/tests/utxo/asserts/asset_id_with_amount.simf b/simf/tests/utxo/asserts/asset_id_with_amount.simf new file mode 100644 index 0000000..7fb2caa --- /dev/null +++ b/simf/tests/utxo/asserts/asset_id_with_amount.simf @@ -0,0 +1,114 @@ +use crate::lib::utxo::asserts::asset_id_with_amount::{ + assert_explicit_asset_id_with_amount, + assert_confidential_asset_id_with_amount, + assert_explicit_input_asset_id_with_amount, + assert_confidential_input_asset_id_with_amount, + assert_explicit_output_asset_id_with_amount, + assert_confidential_output_asset_id_with_amount, + assert_explicit_current_asset_id_with_amount, + assert_confidential_current_asset_id_with_amount +}; + +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let index: u32 = witness::INDEX; + let is_input_index: bool = witness::IS_INPUT_INDEX; + + let expected_asset_id: u256 = witness::EXPECTED_ASSET; + let expected_asset_parity: u1 = witness::EXPECTED_ASSET_PARITY; + let expected_confidential_asset_id: u256 = witness::EXPECTED_CONF_ASSET; + + let expected_confidential_asset_id: Confidential1 = ( + expected_asset_parity, + expected_confidential_asset_id + ); + + let expected_amount: u64 = witness::EXPECTED_AMOUNT; + let expected_amount_parity: u1 = witness::EXPECTED_AMOUNT_PARITY; + let expected_confidential_amount: u256 = witness::EXPECTED_CONF_AMOUNT; + + let expected_confidential_amount: Confidential1 = ( + expected_amount_parity, + expected_confidential_amount + ); + + match is_selected(0, fn_idx) { + true => { + assert_explicit_asset_id_with_amount( + index, + is_input_index, + expected_asset_id, + expected_amount + ); + }, + false => (), + }; + + match is_selected(1, fn_idx) { + true => { + assert_confidential_asset_id_with_amount( + index, + is_input_index, + expected_confidential_asset_id, + expected_confidential_amount + ); + }, + false => (), + }; + + match is_selected(2, fn_idx) { + true => { + assert_explicit_input_asset_id_with_amount(index, expected_asset_id, expected_amount); + }, + false => (), + }; + + match is_selected(3, fn_idx) { + true => { + assert_confidential_input_asset_id_with_amount( + index, + expected_confidential_asset_id, + expected_confidential_amount + ); + }, + false => (), + }; + + match is_selected(4, fn_idx) { + true => { + assert_explicit_output_asset_id_with_amount(index, expected_asset_id, expected_amount); + }, + false => (), + }; + + match is_selected(5, fn_idx) { + true => { + assert_confidential_output_asset_id_with_amount( + index, + expected_confidential_asset_id, + expected_confidential_amount + ); + }, + false => (), + }; + + match is_selected(6, fn_idx) { + true => { + assert_explicit_current_asset_id_with_amount(expected_asset_id, expected_amount); + }, + false => (), + }; + + match is_selected(7, fn_idx) { + true => { + assert_confidential_current_asset_id_with_amount( + expected_confidential_asset_id, + expected_confidential_amount + ); + }, + false => (), + }; +} diff --git a/tests/stdlib/common/utxo_helper.rs b/tests/stdlib/common/utxo_helper.rs index ac94a6d..1cf4714 100644 --- a/tests/stdlib/common/utxo_helper.rs +++ b/tests/stdlib/common/utxo_helper.rs @@ -1,3 +1,4 @@ +use primitive_types::U256; use rand::Rng; use simplex::program::Program; use simplex::simplicityhl::elements::AssetId; @@ -7,6 +8,7 @@ use simplex::simplicityhl::elements::pset::serialize::Serialize; use simplex::transaction::UTXO; use crate::common::core::{send_blinded, send_explicit, send_issued}; +use crate::common::helper::generate_u256; pub const DEFAULT_SEND_AMOUNT: u64 = 50; /// The funding transaction holds around 10^16 satoshis, so 10_000 is used @@ -163,3 +165,21 @@ pub fn search_utxo_by_txid(txid: &Txid, utxos: &[UTXO]) -> anyhow::Result .cloned() .ok_or_else(|| anyhow::anyhow!("Missing utxo with id: {txid}")) } + +pub fn generate_different_amounts() -> (u64, u64) { + let some_u = random_expected_amount(); + let mut other_u = random_expected_amount(); + + while other_u == some_u { + other_u = random_expected_amount(); + } + + (some_u, other_u) +} + +pub fn generate_random_confidential() -> (u8, [u8; 32]) { + let parity_bit = rand::thread_rng().gen_range(0..=1); + let asset = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + + (parity_bit, asset) +} diff --git a/tests/stdlib/utxo/asserts/amount.rs b/tests/stdlib/utxo/asserts/amount.rs new file mode 100644 index 0000000..9930aa1 --- /dev/null +++ b/tests/stdlib/utxo/asserts/amount.rs @@ -0,0 +1,643 @@ +use anyhow::Context; +use rand::Rng; +use simplex::simplicityhl::elements::Txid; + +use crate::common::core::Expect; +use crate::common::core::run_with_inputs_outputs; +use crate::common::utxo_helper::ConfidentialAmount; +use crate::common::utxo_helper::create_utxos; +use crate::common::utxo_helper::generate_different_amounts; +use crate::common::utxo_helper::generate_random_confidential; +use crate::common::utxo_helper::random_expected_amount; + +use simplicityhl_std::artifacts::tests::utxo::asserts::amount::AmountProgram as TestAmountProgram; +use simplicityhl_std::artifacts::tests::utxo::asserts::amount::derived_amount::{ + AmountArguments as TestAmountArguments, AmountWitness as TestAmountWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + Explicit, + Confidential, + ExplicitInput, + ConfidentialInput, + ExplicitOutput, + ConfidentialOutput, + CurrentExplicit, + CurrentConfidential, +} + +#[derive(PartialEq, Eq)] +enum IndexType { + Input, + Output, +} + +fn is_input(o: IndexType) -> bool { + o == IndexType::Input +} + +fn program() -> TestAmountProgram { + TestAmountProgram::new(&TestAmountArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: TestAmountWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: TestAmountWitness { + function_index: function as u8, + index: 0, + is_input_index: false, + expected: 0, + expected_parity: 0, + expected_conf: [0; 32], + }, + } +} + +impl Case { + /// `index`: the input/output the arm inspects. + fn index(mut self, index: u32) -> Self { + self.witness.index = index; + self + } + + /// `is_input`: whether the arm inspects an input. + fn index_type(mut self, index_type: IndexType) -> Self { + self.witness.is_input_index = is_input(index_type); + self + } + + /// `expected`: value the arm should produce. + fn expect(mut self, expected: u64) -> Self { + self.witness.expected = expected; + self + } + + /// `expected_parity`: value the arm should produce. + fn expected_parity(mut self, expected_parity: u8) -> Self { + self.witness.expected_parity = expected_parity; + self + } + + /// `expected_confidential`: the arm's confidential result. + fn confidential_amount(mut self, expected_confidential: [u8; 32]) -> Self { + self.witness.expected_conf = expected_confidential; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext, txids: Vec) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok, txids) + } + + /// Fund, spend, and expect `expect`. + fn expecting( + self, + context: &simplex::TestContext, + expect: Expect, + txids: Vec, + ) -> anyhow::Result<()> { + run_with_inputs_outputs(context, program(), self.witness, expect, txids) + } +} + +/// Missing input or output also returns `Expect::PrunedBranch` error, +/// so this approach ensures that the program has the expected input or output. +/// A failed spend is not broadcasted, so both cases can use the same UTXOs. +fn fail_and_pass_cases( + context: &simplex::TestContext, + failing: Case, + passing: Case, + txids: Vec, +) -> anyhow::Result<()> { + failing.expecting(context, Expect::PrunedBranch, txids.clone())?; + passing.run(context, txids) +} + +fn create_utxos_wrapper( + context: &simplex::TestContext, + index: u32, + is_explicit: bool, + is_input: bool, + expected_amount: u64, +) -> anyhow::Result<(Option, Vec)> { + let (result, txids) = create_utxos( + context, + index, + is_explicit, + is_input, + expected_amount, + program(), + )?; + + let confidential_amount = match is_explicit { + true => None, + false => { + let (_, conf_amount) = + result.context("confidential UTXO creation returned no commitment")?; + + Some(conf_amount) + } + }; + + Ok((confidential_amount, txids)) +} + +#[simplex::test] +fn assert_explicit_amount_for_input(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + let failing = case(Confidential).index(index).index_type(IndexType::Input); + let passing = case(Explicit) + .index(index) + .index_type(IndexType::Input) + .expect(expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_explicit_amount_for_random_input(context: simplex::TestContext) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(0..=20) as u32; // not a big value to not to slow down tests + let expected_amount: u64 = random_expected_amount(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + case(Explicit) + .index(index) + .index_type(IndexType::Input) + .expect(expected_amount) + .run(&context, txids) +} + +#[simplex::test] +fn assert_false_explicit_amount_for_input(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 2; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + real_amount, + )?; + + case(Explicit) + .index(index) + .index_type(IndexType::Input) + .expect(expected_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_amount_for_output(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 1; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + expected_amount, + )?; + + let failing = case(Confidential) + .index(index) + .index_type(IndexType::Output); + let passing = case(Explicit) + .index(index) + .index_type(IndexType::Output) + .expect(expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_explicit_amount_for_random_output(context: simplex::TestContext) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(0..=20) as u32; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + expected_amount, + )?; + + case(Explicit) + .index(index) + .index_type(IndexType::Output) + .expect(expected_amount) + .run(&context, txids) +} + +#[simplex::test] +fn assert_false_explicit_amount_for_output(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 3; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + real_amount, + )?; + + case(Explicit) + .index(index) + .index_type(IndexType::Output) + .expect(expected_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_amount_for_confidential_output_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 0; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (conf, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + expected_amount, + )?; + let conf = conf.unwrap(); + + let failing = case(Explicit).index(index).index_type(IndexType::Output); + // Cannot know confidential value in the output, so check the input instead + let passing = case(Confidential) + .index(index) + .index_type(IndexType::Input) + .expected_parity(conf.parity_bit) + .confidential_amount(conf.amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_confidential_amount_for_input(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 3; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (conf, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + let conf = conf.unwrap(); + + let failing = case(Explicit).index(index).index_type(IndexType::Input); + let passing = case(Confidential) + .index(index) + .index_type(IndexType::Input) + .expected_parity(conf.parity_bit) + .confidential_amount(conf.amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_amount_for_input(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 1; + let expected_amount: u64 = random_expected_amount(); + let (false_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + case(Confidential) + .index(index) + .expected_parity(false_bit) + .confidential_amount(false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_false_confidential_amount_for_output( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let expected_amount: u64 = random_expected_amount(); + let (false_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + expected_amount, + )?; + + case(Confidential) + .index(index) + .expected_parity(false_bit) + .confidential_amount(false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_input_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 1; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + let failing = case(ConfidentialInput).index(index); + let passing = case(ExplicitInput).index(index).expect(expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_explicit_input_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 2; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + real_amount, + )?; + + case(ExplicitInput) + .index(index) + .index_type(IndexType::Input) + .expect(expected_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_confidential_input_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (conf, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + let conf = conf.unwrap(); + + let failing = case(ExplicitInput).index(index); + let passing = case(ConfidentialInput) + .index(index) + .expected_parity(conf.parity_bit) + .confidential_amount(conf.amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_input_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let expected_amount: u64 = random_expected_amount(); + let (false_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + case(ConfidentialInput) + .index(index) + .expected_parity(false_bit) + .confidential_amount(false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_output_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 2; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + expected_amount, + )?; + + let failing = case(ConfidentialOutput).index(index); + let passing = case(ExplicitOutput).index(index).expect(expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_explicit_output_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 2; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + real_amount, + )?; + + case(ExplicitOutput) + .index(index) + .expect(expected_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_output_amount_for_confidential_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 2; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (conf, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + expected_amount, + )?; + let conf = conf.unwrap(); + + let failing = case(ExplicitOutput).index(index); + // Cannot know confidential value in the output, so check the input instead + let passing = case(ConfidentialInput) + .index(index) + .expected_parity(conf.parity_bit) + .confidential_amount(conf.amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_output_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let expected_amount: u64 = random_expected_amount(); + let (false_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + case(ConfidentialOutput) + .index(index) + .expected_parity(false_bit) + .confidential_amount(false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_current_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + let failing = case(CurrentConfidential).index(index); + let passing = case(CurrentExplicit).index(index).expect(expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_explicit_current_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + let is_explicit = true; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + real_amount, + )?; + + case(CurrentExplicit) + .index(index) + .expect(expected_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_confidential_current_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (conf, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + let conf = conf.unwrap(); + + let failing = case(CurrentExplicit).index(index); + let passing = case(CurrentConfidential) + .index(index) + .expected_parity(conf.parity_bit) + .confidential_amount(conf.amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_current_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let expected_amount: u64 = random_expected_amount(); + let (false_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + case(CurrentConfidential) + .index(index) + .expected_parity(false_bit) + .confidential_amount(false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} diff --git a/tests/stdlib/utxo/asserts/asset_id.rs b/tests/stdlib/utxo/asserts/asset_id.rs new file mode 100644 index 0000000..7a4dc6c --- /dev/null +++ b/tests/stdlib/utxo/asserts/asset_id.rs @@ -0,0 +1,470 @@ +use anyhow::Context; +use rand::Rng; +use simplex::simplicityhl::elements::Txid; + +use crate::common::core::Expect; +use crate::common::core::run_with_inputs_outputs; +use crate::common::utxo_helper::ConfidentialAssetId; +use crate::common::utxo_helper::DEFAULT_SEND_AMOUNT; +use crate::common::utxo_helper::create_utxos; +use crate::common::utxo_helper::create_utxos_with_new_asset; +use crate::common::utxo_helper::generate_random_confidential; + +use simplicityhl_std::artifacts::tests::utxo::asserts::asset_id::AssetIdProgram as TestAssetIdProgram; +use simplicityhl_std::artifacts::tests::utxo::asserts::asset_id::derived_asset_id::{ + AssetIdArguments as TestAssetIdArguments, AssetIdWitness as TestAssetIdWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + Explicit, + Confidential, + ExplicitInput, + ConfidentialInput, + ExplicitOutput, + ConfidentialOutput, + CurrentExplicit, + CurrentConfidential, +} + +#[derive(PartialEq, Eq)] +enum IndexType { + Input, + Output, +} + +fn is_input(o: IndexType) -> bool { + o == IndexType::Input +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> TestAssetIdProgram { + TestAssetIdProgram::new(&TestAssetIdArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: TestAssetIdWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: TestAssetIdWitness { + function_index: function as u8, + index: 0, + is_input_index: false, + expected: DEFAULT_EXPECTED, + expected_parity: 0, + expected_conf: DEFAULT_EXPECTED, + }, + } +} + +impl Case { + /// `index`: the input/output the arm inspects. + fn index(mut self, index: u32) -> Self { + self.witness.index = index; + self + } + + /// `is_input`: whether the arm inspects an input. + fn index_type(mut self, index_type: IndexType) -> Self { + self.witness.is_input_index = is_input(index_type); + self + } + + /// `expected`: value the arm should produce. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = expected; + self + } + + /// `expected_parity`: value the arm should produce. + fn expected_parity(mut self, expected_parity: u8) -> Self { + self.witness.expected_parity = expected_parity; + self + } + + /// `expected_confidential`: the arm's confidential result. + fn confidential_asset_id(mut self, expected_confidential: [u8; 32]) -> Self { + self.witness.expected_conf = expected_confidential; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext, txids: Vec) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok, txids) + } + + /// Fund, spend, and expect `expect`. + fn expecting( + self, + context: &simplex::TestContext, + expect: Expect, + txids: Vec, + ) -> anyhow::Result<()> { + run_with_inputs_outputs(context, program(), self.witness, expect, txids) + } +} + +/// Missing input or output also returns `Expect::PrunedBranch` error, +/// so this approach ensures that the program has the expected input or output. +/// A failed spend is not broadcasted, so both cases can use the same UTXOs. +fn fail_and_pass_cases( + context: &simplex::TestContext, + failing: Case, + passing: Case, + txids: Vec, +) -> anyhow::Result<()> { + failing.expecting(context, Expect::PrunedBranch, txids.clone())?; + passing.run(context, txids) +} + +fn create_utxos_wrapper( + context: &simplex::TestContext, + index: u32, + is_explicit: bool, +) -> anyhow::Result<(Option, Vec)> { + let (result, txids) = create_utxos( + context, + index, + is_explicit, + true, // is_input is always true, because this parameter affects only tested amount + DEFAULT_SEND_AMOUNT, + program(), + )?; + + let confidential_asset = match is_explicit { + true => None, + false => { + let (conf_asset, _) = + result.context("confidential UTXO creation returned no commitment")?; + + Some(conf_asset) + } + }; + + Ok((confidential_asset, txids)) +} + +#[simplex::test] +fn assert_explicit_asset_id_for_input(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 1; + let (expected_asset_id, txids) = + create_utxos_with_new_asset(&context, index, true, DEFAULT_SEND_AMOUNT, program())?; + + let failing = case(Confidential).index(index).index_type(IndexType::Input); + let passing = case(Explicit) + .index(index) + .index_type(IndexType::Input) + .expect(expected_asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_explicit_asset_id_for_random_input(context: simplex::TestContext) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(0..=20) as u32; // not a big value to not to slow down tests + let (expected_asset_id, txids) = + create_utxos_with_new_asset(&context, index, true, DEFAULT_SEND_AMOUNT, program())?; + + case(Explicit) + .index(index) + .index_type(IndexType::Input) + .expect(expected_asset_id) + .run(&context, txids) +} + +#[simplex::test] +fn assert_false_explicit_asset_id_for_input(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 2; + let (_, txids) = + create_utxos_with_new_asset(&context, index, true, DEFAULT_SEND_AMOUNT, program())?; + + case(Explicit) + .index(index) + .index_type(IndexType::Input) + .expect(context.get_network().policy_asset().into_inner().0) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_asset_id_for_output(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let (expected_asset_id, txids) = + create_utxos_with_new_asset(&context, index, true, DEFAULT_SEND_AMOUNT, program())?; + + let failing = case(Confidential).index(index); + let passing = case(Explicit).index(index).expect(expected_asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_explicit_asset_id_for_random_output(context: simplex::TestContext) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(0..=20) as u32; + let (expected_asset_id, txids) = + create_utxos_with_new_asset(&context, index, true, DEFAULT_SEND_AMOUNT, program())?; + + case(Explicit) + .index(index) + .index_type(IndexType::Output) + .expect(expected_asset_id) + .run(&context, txids) +} + +#[simplex::test] +fn assert_false_explicit_asset_id_for_output(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 3; + let (_, txids) = + create_utxos_with_new_asset(&context, index, false, DEFAULT_SEND_AMOUNT, program())?; + + case(Explicit) + .index(index) + .index_type(IndexType::Output) + .expect(context.get_network().policy_asset().into_inner().0) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_asset_id_for_confidential_output_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 0; + let is_explicit = false; + + let (conf, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let conf = conf.unwrap(); + + let failing = case(Explicit).index(index).index_type(IndexType::Output); + // Cannot know confidential value in the output, so check the input instead + let passing = case(Confidential) + .index(index) + .index_type(IndexType::Input) + .expected_parity(conf.parity_bit) + .confidential_asset_id(conf.asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_confidential_asset_id_for_input(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 2; + let is_explicit = false; + + let (conf_asset, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let expected = conf_asset.unwrap(); + + let failing = case(Explicit).index(index).index_type(IndexType::Input); + let passing = case(Confidential) + .index(index) + .index_type(IndexType::Input) + .expected_parity(expected.parity_bit) + .confidential_asset_id(expected.asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_asset_id_for_input( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 2; + let is_explicit = false; + let (_, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let (false_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + + case(Confidential) + .index(index) + .index_type(IndexType::Input) + .expected_parity(false_bit) + .confidential_asset_id(false_asset) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_false_confidential_asset_id_for_output( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 2; + let is_explicit = false; + let (_, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let (false_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + + case(Confidential) + .index(index) + .index_type(IndexType::Output) + .expected_parity(false_bit) + .confidential_asset_id(false_asset) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_input_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let (expected_asset_id, txids) = + create_utxos_with_new_asset(&context, index, true, DEFAULT_SEND_AMOUNT, program())?; + + let failing = case(ConfidentialInput).index(index); + let passing = case(ExplicitInput).index(index).expect(expected_asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_explicit_input_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 3; + let (_, txids) = + create_utxos_with_new_asset(&context, index, false, DEFAULT_SEND_AMOUNT, program())?; + + case(ExplicitInput) + .index(index) + .expect(context.get_network().policy_asset().into_inner().0) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_confidential_input_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 1; + let is_explicit = false; + + let (conf_asset, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let expected = conf_asset.unwrap(); + + let failing = case(ExplicitInput).index(index); + let passing = case(ConfidentialInput) + .index(index) + .expected_parity(expected.parity_bit) + .confidential_asset_id(expected.asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_input_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 2; + let is_explicit = false; + let (_, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let (false_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + + case(ConfidentialInput) + .index(index) + .expected_parity(false_bit) + .confidential_asset_id(false_asset) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_output_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 2; + let (expected_asset_id, txids) = + create_utxos_with_new_asset(&context, index, true, DEFAULT_SEND_AMOUNT, program())?; + + let failing = case(ConfidentialOutput).index(index); + let passing = case(ExplicitOutput).index(index).expect(expected_asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_explicit_output_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 3; + let (_, txids) = + create_utxos_with_new_asset(&context, index, false, DEFAULT_SEND_AMOUNT, program())?; + + case(ExplicitOutput) + .index(index) + .expect(context.get_network().policy_asset().into_inner().0) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_output_asset_id_for_confidential_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 2; + let is_explicit = false; + + let (conf, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let conf = conf.unwrap(); + + let failing = case(ExplicitOutput).index(index); + // Cannot know confidential value in the output, so check the input instead + let passing = case(ConfidentialInput) + .index(index) + .expected_parity(conf.parity_bit) + .confidential_asset_id(conf.asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_output_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 2; + let is_explicit = false; + let (_, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let (false_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + + case(ConfidentialOutput) + .index(index) + .expected_parity(false_bit) + .confidential_asset_id(false_asset) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_current_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let (expected_asset_id, txids) = + create_utxos_with_new_asset(&context, index, true, DEFAULT_SEND_AMOUNT, program())?; + + let failing = case(CurrentConfidential).index(index); + let passing = case(CurrentExplicit).index(index).expect(expected_asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_explicit_current_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let (_, txids) = + create_utxos_with_new_asset(&context, index, false, DEFAULT_SEND_AMOUNT, program())?; + + case(CurrentExplicit) + .index(index) + .expect(context.get_network().policy_asset().into_inner().0) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_confidential_current_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let is_explicit = false; + + let (conf_asset, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let expected = conf_asset.unwrap(); + + let failing = case(CurrentExplicit).index(index); + let passing = case(CurrentConfidential) + .index(index) + .expected_parity(expected.parity_bit) + .confidential_asset_id(expected.asset_id); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_current_asset_id(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let is_explicit = false; + let (_, txids) = create_utxos_wrapper(&context, index, is_explicit)?; + let (false_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + + case(CurrentConfidential) + .index(index) + .expected_parity(false_bit) + .confidential_asset_id(false_asset) + .expecting(&context, Expect::AssertFailed, txids) +} diff --git a/tests/stdlib/utxo/asserts/asset_id_with_amount.rs b/tests/stdlib/utxo/asserts/asset_id_with_amount.rs new file mode 100644 index 0000000..d681124 --- /dev/null +++ b/tests/stdlib/utxo/asserts/asset_id_with_amount.rs @@ -0,0 +1,693 @@ +use rand::Rng; +use simplex::simplicityhl::elements::Txid; + +use crate::common::core::Expect; +use crate::common::core::run_with_inputs_outputs; +use crate::common::utxo_helper::ConfidentialAsset; +use crate::common::utxo_helper::create_utxos; +use crate::common::utxo_helper::create_utxos_with_new_asset; +use crate::common::utxo_helper::generate_different_amounts; +use crate::common::utxo_helper::generate_random_confidential; +use crate::common::utxo_helper::random_expected_amount; + +use simplicityhl_std::artifacts::tests::utxo::asserts::asset_id_with_amount::AssetIdWithAmountProgram as TestAssetIdWithAmountProgram; +use simplicityhl_std::artifacts::tests::utxo::asserts::asset_id_with_amount::derived_asset_id_with_amount::{ + AssetIdWithAmountArguments as TestAssetIdWithAmountArguments, AssetIdWithAmountWitness as TestAssetIdWithAmountWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + Explicit, + Confidential, + ExplicitInput, + ConfidentialInput, + ExplicitOutput, + ConfidentialOutput, + CurrentExplicit, + CurrentConfidential, +} + +#[derive(PartialEq, Eq)] +enum IndexType { + Input, + Output, +} + +fn is_input(o: IndexType) -> bool { + o == IndexType::Input +} + +fn program() -> TestAssetIdWithAmountProgram { + TestAssetIdWithAmountProgram::new(&TestAssetIdWithAmountArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: TestAssetIdWithAmountWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: TestAssetIdWithAmountWitness { + function_index: function as u8, + index: 0, + is_input_index: false, + expected_asset: [0; 32], + expected_asset_parity: 0, + expected_conf_asset: [0; 32], + expected_amount: 0, + expected_amount_parity: 0, + expected_conf_amount: [0; 32], + }, + } +} + +impl Case { + /// `index`: the input/output the arm inspects. + fn index(mut self, index: u32) -> Self { + self.witness.index = index; + self + } + + /// `is_input`: whether the arm inspects an input. + fn index_type(mut self, index_type: IndexType) -> Self { + self.witness.is_input_index = is_input(index_type); + self + } + + /// `expected`: value the arm should produce. + fn expect(mut self, expected_asset: [u8; 32], expected_amount: u64) -> Self { + self.witness.expected_asset = expected_asset; + self.witness.expected_amount = expected_amount; + self + } + + /// `expected_parity`: value the arm should produce. + fn expected_parity(mut self, expected_asset_parity: u8, expected_amount_parity: u8) -> Self { + self.witness.expected_asset_parity = expected_asset_parity; + self.witness.expected_amount_parity = expected_amount_parity; + self + } + + /// `expected_conf_asset` and `expected_conf_amount`. + fn confidentials( + mut self, + expected_conf_asset: [u8; 32], + expected_conf_amount: [u8; 32], + ) -> Self { + self.witness.expected_conf_asset = expected_conf_asset; + self.witness.expected_conf_amount = expected_conf_amount; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext, txids: Vec) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok, txids) + } + + /// Fund, spend, and expect `expect`. + fn expecting( + self, + context: &simplex::TestContext, + expect: Expect, + txids: Vec, + ) -> anyhow::Result<()> { + run_with_inputs_outputs(context, program(), self.witness, expect, txids) + } +} + +/// Missing input or output also returns `Expect::PrunedBranch` error, +/// so this approach ensures that the program has the expected input or output. +/// A failed spend is not broadcasted, so both cases can use the same UTXOs. +fn fail_and_pass_cases( + context: &simplex::TestContext, + failing: Case, + passing: Case, + txids: Vec, +) -> anyhow::Result<()> { + failing.expecting(context, Expect::PrunedBranch, txids.clone())?; + passing.run(context, txids) +} + +fn create_utxos_wrapper( + context: &simplex::TestContext, + index: u32, + is_explicit: bool, + is_input: bool, + expected_amount: u64, +) -> anyhow::Result<(Option, Vec)> { + create_utxos( + context, + index, + is_explicit, + is_input, + expected_amount, + program(), + ) +} + +#[simplex::test] +fn assert_explicit_asset_id_with_amount_for_input( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 0; + let expected_amount = random_expected_amount(); + let (expected_asset_id, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Input), + expected_amount, + program(), + )?; + + let failing = case(Confidential).index(index).index_type(IndexType::Input); + let passing = case(Explicit) + .index(index) + .index_type(IndexType::Input) + .expect(expected_asset_id, expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_explicit_asset_id_with_amount_for_random_input( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(0..=20) as u32; + let expected_amount = random_expected_amount(); + let (expected_asset_id, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Input), + expected_amount, + program(), + )?; + + case(Explicit) + .index(index) + .index_type(IndexType::Input) + .expect(expected_asset_id, expected_amount) + .run(&context, txids) +} + +#[simplex::test] +fn assert_false_explicit_asset_id_with_amount_for_input( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + + let (_, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Input), + expected_amount, + program(), + )?; + + case(Explicit) + .index(index) + .index_type(IndexType::Input) + .expect( + context.get_network().policy_asset().into_inner().0, + real_amount, + ) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_asset_id_with_amount_for_output( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let expected_amount: u64 = random_expected_amount(); + let (expected_asset_id, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Output), + expected_amount, + program(), + )?; + + let failing = case(Confidential) + .index(index) + .index_type(IndexType::Output); + let passing = case(Explicit) + .index(index) + .index_type(IndexType::Output) + .expect(expected_asset_id, expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_explicit_asset_id_with_amount_for_random_output( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(0..=20) as u32; + let expected_amount = random_expected_amount(); + let (expected_asset_id, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Output), + expected_amount, + program(), + )?; + + case(Explicit) + .index(index) + .index_type(IndexType::Output) + .expect(expected_asset_id, expected_amount) + .run(&context, txids) +} + +#[simplex::test] +fn assert_false_explicit_asset_id_with_amount_for_output( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 2; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + + let (_, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Output), + expected_amount, + program(), + )?; + + case(Explicit) + .index(index) + .index_type(IndexType::Output) + .expect( + context.get_network().policy_asset().into_inner().0, + real_amount, + ) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_asset_id_with_amount_for_confidential_output_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (conf, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + expected_amount, + )?; + let (asset, amount) = conf.unwrap(); + + let failing = case(Explicit).index(index).index_type(IndexType::Output); + // Cannot know confidential value in the output, so check the input instead + let passing = case(Confidential) + .index(index) + .index_type(IndexType::Input) + .expected_parity(asset.parity_bit, amount.parity_bit) + .confidentials(asset.asset_id, amount.amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_confidential_asset_id_with_amount_for_input( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 2; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (expected, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + let (asset, amount) = expected.unwrap(); + + let failing = case(Explicit).index(index).index_type(IndexType::Input); + let passing = case(Confidential) + .index(index) + .index_type(IndexType::Input) + .expected_parity(asset.parity_bit, amount.parity_bit) + .confidentials(asset.asset_id, amount.amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_asset_id_with_amount_for_input( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + random_expected_amount(), + )?; + + let (false_asset_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + let (false_amount_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + + case(Confidential) + .index(index) + .index_type(IndexType::Input) + .expected_parity(false_asset_bit, false_amount_bit) + .confidentials(false_asset, false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_false_confidential_asset_id_with_amount_for_output( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + random_expected_amount(), + )?; + + let (false_asset_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + let (false_amount_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + + case(Confidential) + .index(index) + .index_type(IndexType::Output) + .expected_parity(false_asset_bit, false_amount_bit) + .confidentials(false_asset, false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_input_asset_id_with_amount(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + let expected_amount = random_expected_amount(); + let (expected_asset_id, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Input), + expected_amount, + program(), + )?; + + let failing = case(ConfidentialInput).index(index); + let passing = case(ExplicitInput) + .index(index) + .expect(expected_asset_id, expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_explicit_input_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + + let (_, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Input), + expected_amount, + program(), + )?; + + case(ExplicitInput) + .index(index) + .expect( + context.get_network().policy_asset().into_inner().0, + real_amount, + ) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_confidential_input_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (expected, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + let (conf_asset, conf_amount) = expected.unwrap(); + + let (asset_parity_bit, asset_conf_amount) = (conf_asset.parity_bit, conf_asset.asset_id); + let (amount_parity_bit, amount_conf_amount) = (conf_amount.parity_bit, conf_amount.amount); + + let failing = case(ExplicitInput).index(index); + let passing = case(ConfidentialInput) + .index(index) + .expected_parity(asset_parity_bit, amount_parity_bit) + .confidentials(asset_conf_amount, amount_conf_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_input_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + random_expected_amount(), + )?; + + let (false_asset_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + let (false_amount_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + + case(ConfidentialInput) + .index(index) + .expected_parity(false_asset_bit, false_amount_bit) + .confidentials(false_asset, false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_output_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let expected_amount = random_expected_amount(); + let (expected_asset_id, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Output), + expected_amount, + program(), + )?; + + let failing = case(ConfidentialOutput).index(index); + let passing = case(ExplicitOutput) + .index(index) + .expect(expected_asset_id, expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_explicit_output_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 2; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + + let (_, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Output), + expected_amount, + program(), + )?; + + case(ExplicitOutput) + .index(index) + .expect( + context.get_network().policy_asset().into_inner().0, + real_amount, + ) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_false_confidential_output_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 2; + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + random_expected_amount(), + )?; + + let (false_asset_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + let (false_amount_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + + case(ConfidentialOutput) + .index(index) + .expected_parity(false_asset_bit, false_amount_bit) + .confidentials(false_asset, false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_explicit_output_asset_id_with_amount_for_confidential_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 1; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (conf, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Output), + expected_amount, + )?; + let (asset, amount) = conf.unwrap(); + + let failing = case(ExplicitOutput).index(index); + // Cannot know confidential value in the output, so check the input instead + let passing = case(ConfidentialInput) + .index(index) + .expected_parity(asset.parity_bit, amount.parity_bit) + .confidentials(asset.asset_id, amount.amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_explicit_current_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 0; + let expected_amount = random_expected_amount(); + let (expected_asset_id, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Input), + expected_amount, + program(), + )?; + + let failing = case(CurrentConfidential).index(index); + let passing = case(CurrentExplicit) + .index(index) + .expect(expected_asset_id, expected_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_explicit_current_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 2; + let (real_amount, expected_amount): (u64, u64) = generate_different_amounts(); + + let (_, txids) = create_utxos_with_new_asset( + &context, + index, + is_input(IndexType::Input), + expected_amount, + program(), + )?; + + case(CurrentExplicit) + .index(index) + .expect( + context.get_network().policy_asset().into_inner().0, + real_amount, + ) + .expecting(&context, Expect::AssertFailed, txids) +} + +#[simplex::test] +fn assert_confidential_current_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 0; + let expected_amount: u64 = random_expected_amount(); + let is_explicit = false; + + let (expected, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + expected_amount, + )?; + + let (conf_asset, conf_amount) = expected.unwrap(); + + let (asset_parity_bit, asset_conf_amount) = (conf_asset.parity_bit, conf_asset.asset_id); + let (amount_parity_bit, amount_conf_amount) = (conf_amount.parity_bit, conf_amount.amount); + + let failing = case(CurrentExplicit).index(index); + let passing = case(CurrentConfidential) + .index(index) + .expected_parity(asset_parity_bit, amount_parity_bit) + .confidentials(asset_conf_amount, amount_conf_amount); + + fail_and_pass_cases(&context, failing, passing, txids) +} + +#[simplex::test] +fn assert_false_confidential_current_asset_id_with_amount( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = 0; + let is_explicit = false; + + let (_, txids) = create_utxos_wrapper( + &context, + index, + is_explicit, + is_input(IndexType::Input), + random_expected_amount(), + )?; + + let (false_asset_bit, false_asset): (u8, [u8; 32]) = generate_random_confidential(); + let (false_amount_bit, false_amount): (u8, [u8; 32]) = generate_random_confidential(); + + case(CurrentConfidential) + .index(index) + .expected_parity(false_asset_bit, false_amount_bit) + .confidentials(false_asset, false_amount) + .expecting(&context, Expect::AssertFailed, txids) +} diff --git a/tests/stdlib/utxo/asserts/mod.rs b/tests/stdlib/utxo/asserts/mod.rs new file mode 100644 index 0000000..6cbabb2 --- /dev/null +++ b/tests/stdlib/utxo/asserts/mod.rs @@ -0,0 +1,3 @@ +mod amount; +mod asset_id; +mod asset_id_with_amount; diff --git a/tests/stdlib/utxo/mod.rs b/tests/stdlib/utxo/mod.rs index 518a791..9c68058 100644 --- a/tests/stdlib/utxo/mod.rs +++ b/tests/stdlib/utxo/mod.rs @@ -1 +1,2 @@ +mod asserts; mod getters;