Skip to content

Commit bfe8e8f

Browse files
Merge pull request #52 from SundaeSwap-finance/ignaciodopazo/has_expected_pool_value-optimization
Resolve SSW-312 (`has_expected_pool_value` traverses output value just once)
2 parents b75bcdf + 5513af3 commit bfe8e8f

File tree

2 files changed

+103
-33
lines changed

2 files changed

+103
-33
lines changed

validators/pool.ak

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@ use aiken/transaction.{
1010
use aiken/transaction/credential.{
1111
Address, Inline, ScriptCredential,
1212
}
13-
use aiken/transaction/value.{MintedValue, PolicyId, Value, ada_policy_id}
13+
use aiken/transaction/value.{MintedValue, PolicyId, Value, ada_policy_id, ada_asset_name}
1414
use calculation/process.{pool_input_to_state, process_orders}
1515
use calculation/shared.{PoolState} as calc_shared
16-
use shared.{
17-
AssetClass, Ident, count_orders, has_exact_token_count, pool_lp_name,
18-
pool_nft_name, spent_output,
19-
}
16+
use shared.{AssetClass, Ident, spent_output, pool_nft_name, pool_lp_name, count_orders}
2017
use sundae/multisig
2118
use types/pool.{
2219
CreatePool, MintLP, PoolDatum, PoolMintRedeemer, PoolRedeemer, PoolScoop,
@@ -519,7 +516,7 @@ fn minted_correct_pool_tokens(
519516

520517
/// Check that the UTXO contents are correct given a specific pool outcome
521518
/// In particular, it must have the final A reserves, the final B reserves, the pool NFT, and the protocol fees
522-
fn has_expected_pool_value(
519+
pub fn has_expected_pool_value(
523520
pool_script_hash: PolicyId,
524521
identifier: Ident,
525522
output_value: Value,
@@ -531,33 +528,48 @@ fn has_expected_pool_value(
531528
let (quantity_b_policy_id, quantity_b_name, quantity_b_amt) = quantity_b
532529
// Asset A *could* be ADA; in which case there should be 3 tokens on the output
533530
// (ADA, Asset B, and the NFT)
534-
// We do this as an optimization, since constructing values is expensive
535-
// OPTIMIZATION: is it possible to check this with a single traversal? Each quantity_of represents a small linear scan
536531
if quantity_a_policy_id == ada_policy_id {
537-
and {
538-
has_exact_token_count(output_value, 3),
539-
value.lovelace_of(output_value) == final_protocol_fees + quantity_a_amt,
540-
value.quantity_of(output_value, quantity_b_policy_id, quantity_b_name) == quantity_b_amt,
541-
value.quantity_of(
542-
output_value,
543-
pool_script_hash,
544-
pool_nft_name(identifier),
545-
) == 1,
546-
}
532+
// Rather than constructing a value directly (which can be expensive)
533+
// we can just compare the expected token count and amounts with a single pass over the value
534+
(3, final_protocol_fees + quantity_a_amt, quantity_b_amt, 1) ==
535+
list.foldl(
536+
value.flatten(output_value),
537+
// (token count, lovelace amount, token b amount, pool nft amount)
538+
(0, 0, 0, 0),
539+
fn (asset, acc) {
540+
let token_count = acc.1st + 1
541+
if asset.1st == quantity_a_policy_id {
542+
(token_count, acc.2nd + asset.3rd, acc.3rd, acc.4th)
543+
} else if asset.1st == quantity_b_policy_id && asset.2nd == quantity_b_name {
544+
(token_count, acc.2nd, acc.3rd + asset.3rd, acc.4th)
545+
} else {
546+
expect asset == (pool_script_hash, pool_nft_name(identifier), 1)
547+
(token_count, acc.2nd, acc.3rd, acc.4th + 1)
548+
}
549+
}
550+
)
547551
} else {
548-
// Otherwise, we expect 4 tokens (ADA, Asset A, Asset B, and the NFT)
549-
// and the corresponding values
550-
and {
551-
has_exact_token_count(output_value, 4),
552-
value.lovelace_of(output_value) == final_protocol_fees,
553-
value.quantity_of(output_value, quantity_a_policy_id, quantity_a_name) == quantity_a_amt,
554-
value.quantity_of(output_value, quantity_b_policy_id, quantity_b_name) == quantity_b_amt,
555-
value.quantity_of(
556-
output_value,
557-
pool_script_hash,
558-
pool_nft_name(identifier),
559-
) == 1,
560-
}
552+
// Asset A isn't ADA, Asset B will *never* be ADA; in this case, there should be 4 tokens on the output:
553+
// ADA, the Pool NFT, Asset A, and Asset B
554+
(4, final_protocol_fees, quantity_a_amt, quantity_b_amt, 1) ==
555+
list.foldl(
556+
value.flatten(output_value),
557+
// (token count, lovelace amount, token a amount, token b amount, pool nft amount)
558+
(0, 0, 0, 0, 0),
559+
fn (asset, acc) {
560+
let token_count = acc.1st + 1
561+
if asset.1st == ada_policy_id && asset.2nd == ada_asset_name {
562+
(token_count, acc.2nd + asset.3rd, acc.3rd, acc.4th, acc.5th)
563+
} else if asset.1st == quantity_a_policy_id && asset.2nd == quantity_a_name {
564+
(token_count, acc.2nd, acc.3rd + asset.3rd, acc.4th, acc.5th)
565+
} else if asset.1st == quantity_b_policy_id && asset.2nd == quantity_b_name {
566+
(token_count, acc.2nd, acc.3rd, acc.4th + asset.3rd, acc.5th)
567+
} else {
568+
expect asset == (pool_script_hash, pool_nft_name(identifier), 1)
569+
(token_count, acc.2nd, acc.3rd, acc.4th, acc.5th + 1)
570+
}
571+
}
572+
)
561573
}
562574
}
563575

@@ -576,4 +588,4 @@ fn compare_asset_class(a: AssetClass, b: AssetClass) {
576588
pub fn int_to_ident(n: Int) -> Ident {
577589
expect n < 256
578590
bytearray.push(#"", n)
579-
}
591+
}

validators/tests/pool.ak

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use aiken/transaction.{
1111
use aiken/transaction/credential.{
1212
Address, VerificationKeyCredential, from_verification_key, from_script, with_delegation_key
1313
}
14-
use aiken/transaction/value.{Value}
14+
use aiken/transaction/value.{Value, ada_policy_id, ada_asset_name}
1515
use shared.{
1616
pool_lp_name,
1717
}
@@ -24,6 +24,7 @@ use types/order.{Deposit, Destination, OrderDatum, Swap}
2424
use types/pool.{
2525
CreatePool, PoolDatum, PoolScoop,
2626
}
27+
use calculation/shared.{PoolState} as calc_shared
2728
use types/settings.{SettingsDatum}
2829
use tx_util/builder.{add_asset_to_tx_output, add_tx_input, add_tx_output, add_tx_ref_input, build_txn_context, mint_assets, new_tx_input, new_tx_output}
2930
use pool as pool_validator
@@ -693,4 +694,61 @@ test mint_test_nonvoid_datum() fail {
693694
identity
694695
)
695696
minted
697+
}
698+
699+
test has_expected_pool_value_test() {
700+
// ADA/rberry pool
701+
let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77"
702+
let rberry_token_name = #"524245525259"
703+
704+
let pool_script_hash = #"00000000000000000000000000000000000000000000000000000000"
705+
let identifier = #"00000000000000000000000000000000000000000000000000000000"
706+
let (_, pool_nft_token, pool_lp_token) = shared.pool_token_names(identifier)
707+
let pool_value = value.from_lovelace(102_000_000)
708+
|> value.add(rberry_policy_id, rberry_token_name, 100_000_000)
709+
|> value.add(pool_script_hash, pool_nft_token, 1)
710+
let outcome = PoolState {
711+
quantity_lp: (pool_script_hash, pool_lp_token, 99),
712+
quantity_a: (ada_policy_id, ada_asset_name, 100_000_000),
713+
quantity_b: (rberry_policy_id, rberry_token_name, 100_000_000)
714+
}
715+
let protocol_fees = 2_000_000
716+
717+
pool_validator.has_expected_pool_value(
718+
pool_script_hash,
719+
identifier,
720+
pool_value,
721+
outcome,
722+
protocol_fees,
723+
)
724+
}
725+
726+
test has_expected_pool_value_test2() {
727+
// other/rberry pool
728+
let other_policy_id = #"fafafafafafafafafafafafafafafafafafafafafafafafafafafafa"
729+
let other_token_name = #"fafafafa"
730+
let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77"
731+
let rberry_token_name = #"524245525259"
732+
733+
let pool_script_hash = #"00000000000000000000000000000000000000000000000000000000"
734+
let identifier = #"00000000000000000000000000000000000000000000000000000000"
735+
let (_, pool_nft_token, pool_lp_token) = shared.pool_token_names(identifier)
736+
let pool_value = value.from_lovelace(2_000_000)
737+
|> value.add(other_policy_id, other_token_name, 100_000_000)
738+
|> value.add(rberry_policy_id, rberry_token_name, 100_000_000)
739+
|> value.add(pool_script_hash, pool_nft_token, 1)
740+
let outcome = PoolState {
741+
quantity_lp: (pool_script_hash, pool_lp_token, 99),
742+
quantity_a: (other_policy_id, other_token_name, 100_000_000),
743+
quantity_b: (rberry_policy_id, rberry_token_name, 100_000_000)
744+
}
745+
let protocol_fees = 2_000_000
746+
747+
pool_validator.has_expected_pool_value(
748+
pool_script_hash,
749+
identifier,
750+
pool_value,
751+
outcome,
752+
protocol_fees,
753+
)
696754
}

0 commit comments

Comments
 (0)