Skip to content

Commit

Permalink
circuits: zk-circuits: valid-match-settle: Add match unit tests
Browse files Browse the repository at this point in the history
These tests are moved from the integration tests, they belong better
as unit tests for testing circuit boundary cases.
  • Loading branch information
joeykraut committed Nov 25, 2023
1 parent ff25d92 commit c711a92
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 349 deletions.
348 changes: 0 additions & 348 deletions circuits/integration/zk_circuits/valid_match_mpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,356 +336,8 @@ async fn test_valid_match__non_integral_price(test_args: IntegrationTestArgs) ->
Ok(())
}

/// Test the case in which the two parties attempt to match on different mints
async fn test_valid_match__different_mints(test_args: IntegrationTestArgs) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let price = *DUMMY_PRICE;
let mut my_order = create_test_order(party_id);
let my_balance = create_test_balance(party_id);
let amount = compute_max_amount(price, &my_order, &my_balance);

// One party switches the quote mint of their order
if fabric.party_id() == PARTY0 {
my_order.quote_mint = 42u8.into();
}

// Validate that the constraints are not satisfied
let witness = setup_witness(
price,
amount,
my_order,
my_balance.clone(),
test_args.mpc_fabric.clone(),
)
.await;
if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

// Now test with base mint switched
let mut my_order = create_test_order(party_id);
if fabric.party_id() == PARTY0 {
my_order.base_mint = 42u8.into();
}

// Validate that the constraints are not satisfied
let witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;
if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

/// Test the case in which the parties sit on the same side of the book
async fn test_valid_match__same_side(test_args: IntegrationTestArgs) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let price = *DUMMY_PRICE;
let mut my_order = create_test_order(party_id);
let my_balance = create_test_balance(party_id);
let amount = compute_max_amount(price, &my_order, &my_balance);

// Switch the side of the market that the first party sits on
if fabric.party_id() == PARTY0 {
my_order.side = my_order.side.opposite();
}

// Validate that the constraints are not satisfied
let witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;
if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

/// Test the case in which the balance provided to the matching engine is not
/// for the correct asset
async fn test_valid_match__invalid_balance_mint(test_args: IntegrationTestArgs) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let price = *DUMMY_PRICE;
let my_order = create_test_order(party_id);
let mut my_balance = create_test_balance(party_id);
let amount = compute_max_amount(price, &my_order, &my_balance);

// Switch the mint of the balance to be the wrong asset in the pair
if party_id == PARTY0 {
if my_balance.mint == my_order.quote_mint {
my_balance.mint = my_order.base_mint.clone();
} else {
my_balance.mint = my_order.quote_mint.clone();
}
}

// Validate that the constraints are not satisfied
let witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;
if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

/// Test the case in which the balance provided does not cover the advertised
/// amount
async fn test_valid_match__insufficient_balance(test_args: IntegrationTestArgs) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let price = *DUMMY_PRICE;
let my_order = create_test_order(party_id);
let mut my_balance = create_test_balance(party_id);
let amount = compute_max_amount(price, &my_order, &my_balance);

// Reduce the balance to be less than the amount
if my_balance.mint == my_order.base_mint {
my_balance.amount = 1;
}

// Validate that the constraints are not satisfied
let witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;
if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

/// Test the case in which the matched amount exceeds the order size for a party
async fn test_valid_match__amount_exceeds_order(test_args: IntegrationTestArgs) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let price = *DUMMY_PRICE;
let my_order = create_test_order(party_id);
let my_balance = create_test_balance(party_id);

// Both parties give an excessive amount so that the minimum amount certainly
// exceeds the order size
let amount = my_order.amount + 1;

// Validate that the constraints are not satisfied
let witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;
if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

/// Test the case in which the `max_minus_min` field is incorrectly computed
async fn test_valid_match__incorrect_max_minus_min(test_args: IntegrationTestArgs) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let price = *DUMMY_PRICE;
let my_order = create_test_order(party_id);
let my_balance = create_test_balance(party_id);
let amount = compute_max_amount(price, &my_order, &my_balance);

// Validate that the constraints are not satisfied
let mut witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;
let mut rng = thread_rng();
witness.match_res.max_minus_min_amount = Scalar::random(&mut rng)
.to_linkable()
.allocate(PARTY0, fabric);

if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

/// Test the case in which the `max_minus_min` field is switched to be
/// `min - max` (i.e. negative)
async fn test_valid_match__max_minus_min_negative(test_args: IntegrationTestArgs) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let price = *DUMMY_PRICE;
let my_order = create_test_order(party_id);
let my_balance = create_test_balance(party_id);
let amount = compute_max_amount(price, &my_order, &my_balance);

let party0_amount = amount
.share_public(PARTY0, test_args.mpc_fabric.clone())
.await;
let party1_amount = amount
.share_public(PARTY1, test_args.mpc_fabric.clone())
.await;
let min_minus_max_amount = Scalar::from(cmp::min(party0_amount, party1_amount))
- Scalar::from(cmp::max(party0_amount, party1_amount));

// Validate that the constraints are not satisfied
let mut witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;
witness.match_res.max_minus_min_amount =
min_minus_max_amount.to_linkable().allocate(PARTY0, fabric);

if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

/// Test the case in which the `min_amount_order_index` field is incorrectly
/// computed
async fn test_valid_match__incorrect_min_amount_order_index(
test_args: IntegrationTestArgs,
) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let price = *DUMMY_PRICE;
let my_order = create_test_order(party_id);
let my_balance = create_test_balance(party_id);
let amount = compute_max_amount(price, &my_order, &my_balance);

// Validate that the constraints are not satisfied
let mut witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;
let min_order_index = witness.match_res.min_amount_order_index.open().await?.val;

// Invert the index
witness.match_res.min_amount_order_index = (Scalar::one() - min_order_index)
.to_linkable()
.allocate(PARTY0, fabric);

if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

/// Test the case in which the execution price exceeds the buy side price
/// protection
async fn test_valid_match__price_protection_violated_buy_side(
test_args: IntegrationTestArgs,
) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let my_order = create_test_order(party_id);
let my_balance = create_test_balance(party_id);

// Execution price exceeds the buy side maximum price
let price = *DUMMY_BUY_SIDE_WORST_PRICE + FixedPoint::from_integer(1);
let amount = compute_max_amount(price, &my_order, &my_balance);

// Validate that the constraints are not satisfied
let witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;

if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

/// Test the case in which the execution price falls sort of the sell
/// side price protection
async fn test_valid_match__price_protection_violated_sell_side(
test_args: IntegrationTestArgs,
) -> Result<()> {
let fabric = &test_args.mpc_fabric;
let party_id = fabric.party_id();
let my_order = create_test_order(party_id);
let my_balance = create_test_balance(party_id);

// Execution price falls short of the sell side minimum price
let price = *DUMMY_SELL_SIDE_WORST_PRICE - FixedPoint::from_integer(1);
let amount = compute_max_amount(price, &my_order, &my_balance);

// Validate that the constraints are not satisfied
let witness = setup_witness(
price,
amount,
my_order,
my_balance,
test_args.mpc_fabric.clone(),
)
.await;

if constraints_satisfied(witness, test_args.mpc_fabric.clone()).await? {
return Err(eyre!("Constraints satisfied on invalid witness"));
}

Ok(())
}

// Take inventory
integration_test_async!(test_valid_match_mpc_valid);
integration_test_async!(test_valid_match_undercapitalized__base_side);
integration_test_async!(test_valid_match_undercapitalized__quote_side);
integration_test_async!(test_valid_match__non_integral_price);
integration_test_async!(test_valid_match__different_mints);
integration_test_async!(test_valid_match__same_side);
integration_test_async!(test_valid_match__invalid_balance_mint);
integration_test_async!(test_valid_match__insufficient_balance);
integration_test_async!(test_valid_match__amount_exceeds_order);
integration_test_async!(test_valid_match__incorrect_max_minus_min);
integration_test_async!(test_valid_match__max_minus_min_negative);
integration_test_async!(test_valid_match__incorrect_min_amount_order_index);
integration_test_async!(test_valid_match__price_protection_violated_buy_side);
integration_test_async!(test_valid_match__price_protection_violated_sell_side);
Loading

0 comments on commit c711a92

Please sign in to comment.