Skip to content

Commit 0de5761

Browse files
authored
Merge pull request #602 from Lekan101/fix/issues-587-597-598-600
feat: add read_creator_supply helper, storage key collision tests, and integration tests
2 parents 4d3e8de + d3e5aec commit 0de5761

4 files changed

Lines changed: 724 additions & 3 deletions

File tree

‎creator-keys/src/lib.rs‎

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,15 @@ pub fn read_registered_creator_profile(
751751
/// Use this helper wherever repeated key balance read logic is needed to keep
752752
/// missing-balance behavior consistent across the contract.
753753
pub fn read_key_balance(env: &Env, creator: &Address) -> u32 {
754-
read_creator_profile(env, creator)
754+
read_creator_supply(env, creator)
755+
}
756+
757+
/// Reads a creator's current key supply from persistent storage.
758+
///
759+
/// Returns `0` if the creator has not been registered (no supply record exists).
760+
/// Centralises the supply read so the storage key format is defined once.
761+
pub fn read_creator_supply(env: &Env, creator_id: &Address) -> u32 {
762+
read_creator_profile(env, creator_id)
755763
.map(|p| p.supply)
756764
.unwrap_or(0)
757765
}
@@ -2181,9 +2189,9 @@ impl CreatorKeysContract {
21812189
/// Read-only view: returns the total key supply for a creator.
21822190
///
21832191
/// Returns `0` if the creator is not registered, avoiding panics for
2184-
/// invalid lookups. Delegates to the shared [`read_key_balance`] helper.
2192+
/// invalid lookups. Delegates to the shared [`read_creator_supply`] helper.
21852193
pub fn get_total_key_supply(env: Env, creator: Address) -> u32 {
2186-
read_key_balance(&env, &creator)
2194+
read_creator_supply(&env, &creator)
21872195
}
21882196

21892197
/// Read-only view: returns the current supply for a registered creator.
@@ -3627,6 +3635,78 @@ mod tests {
36273635
let result = super::validate_non_zero_address(&env, &valid);
36283636
assert_eq!(result, Ok(()));
36293637
}
3638+
3639+
// --- read_creator_supply helper tests (#587) ---
3640+
3641+
#[test]
3642+
fn test_read_creator_supply_returns_correct_supply_for_initialized_creator() {
3643+
use soroban_sdk::{testutils::Address as _, Address, Env};
3644+
3645+
let env = Env::default();
3646+
let creator = Address::generate(&env);
3647+
let contract_id = env.register(super::CreatorKeysContract, ());
3648+
3649+
let profile = super::CreatorProfile {
3650+
creator: creator.clone(),
3651+
handle: soroban_sdk::String::from_str(&env, "alice"),
3652+
supply: 42,
3653+
holder_count: 5,
3654+
fee_recipient: creator.clone(),
3655+
registered_at: 0,
3656+
};
3657+
3658+
let supply = env.as_contract(&contract_id, || {
3659+
env.storage()
3660+
.persistent()
3661+
.set(&super::constants::storage::creator(&creator), &profile);
3662+
3663+
super::read_creator_supply(&env, &creator)
3664+
});
3665+
assert_eq!(supply, 42);
3666+
}
3667+
3668+
#[test]
3669+
fn test_read_creator_supply_returns_zero_for_uninitialized_creator() {
3670+
use soroban_sdk::{testutils::Address as _, Address, Env};
3671+
3672+
let env = Env::default();
3673+
let missing_creator = Address::generate(&env);
3674+
let contract_id = env.register(super::CreatorKeysContract, ());
3675+
3676+
let supply = env.as_contract(&contract_id, || {
3677+
super::read_creator_supply(&env, &missing_creator)
3678+
});
3679+
assert_eq!(supply, 0);
3680+
}
3681+
3682+
#[test]
3683+
fn test_read_creator_supply_matches_read_key_balance() {
3684+
use soroban_sdk::{testutils::Address as _, Address, Env};
3685+
3686+
let env = Env::default();
3687+
let creator = Address::generate(&env);
3688+
let contract_id = env.register(super::CreatorKeysContract, ());
3689+
3690+
let profile = super::CreatorProfile {
3691+
creator: creator.clone(),
3692+
handle: soroban_sdk::String::from_str(&env, "alice"),
3693+
supply: 15,
3694+
holder_count: 3,
3695+
fee_recipient: creator.clone(),
3696+
registered_at: 0,
3697+
};
3698+
3699+
env.as_contract(&contract_id, || {
3700+
env.storage()
3701+
.persistent()
3702+
.set(&super::constants::storage::creator(&creator), &profile);
3703+
3704+
let supply_from_new = super::read_creator_supply(&env, &creator);
3705+
let supply_from_old = super::read_key_balance(&env, &creator);
3706+
assert_eq!(supply_from_new, supply_from_old);
3707+
assert_eq!(supply_from_new, 15);
3708+
});
3709+
}
36303710
}
36313711

36323712
#[cfg(test)]

‎creator-keys/src/test_issues.rs‎

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,4 +450,255 @@ mod issue_tests {
450450
fn test_bonding_curve_step_5() {
451451
test_bonding_curve_step_helper(5, 100, 150, 350);
452452
}
453+
454+
// =========================================================================
455+
// Storage key collision tests (#597)
456+
//
457+
// Confirm that two distinct inputs never produce the same storage key.
458+
// =========================================================================
459+
460+
#[test]
461+
fn test_holder_balance_key_different_holders_differ_for_same_creator() {
462+
let env = Env::default();
463+
let creator = Address::generate(&env);
464+
let holder_a = Address::generate(&env);
465+
let holder_b = Address::generate(&env);
466+
467+
let key_a = constants::storage::holder_balance_key(&creator, &holder_a);
468+
let key_b = constants::storage::holder_balance_key(&creator, &holder_b);
469+
470+
assert_ne!(
471+
key_a, key_b,
472+
"different holders must produce different keys"
473+
);
474+
}
475+
476+
#[test]
477+
fn test_holder_balance_key_argument_order_matters() {
478+
let env = Env::default();
479+
let addr_a = Address::generate(&env);
480+
let addr_b = Address::generate(&env);
481+
482+
let key_ab = constants::storage::holder_balance_key(&addr_a, &addr_b);
483+
let key_ba = constants::storage::holder_balance_key(&addr_b, &addr_a);
484+
485+
assert_ne!(
486+
key_ab, key_ba,
487+
"swapping creator and holder must produce different keys"
488+
);
489+
}
490+
491+
#[test]
492+
fn test_holder_balance_key_never_equals_creator_key() {
493+
let env = Env::default();
494+
let creator = Address::generate(&env);
495+
let holder = Address::generate(&env);
496+
497+
let balance_key = constants::storage::holder_balance_key(&creator, &holder);
498+
let creator_key = constants::storage::creator(&creator);
499+
500+
assert_ne!(
501+
balance_key, creator_key,
502+
"holder balance key must not collide with creator profile key"
503+
);
504+
}
505+
506+
#[test]
507+
fn test_holder_balance_key_never_equals_dividend_accumulator_key() {
508+
let env = Env::default();
509+
let creator = Address::generate(&env);
510+
let holder = Address::generate(&env);
511+
512+
let balance_key = constants::storage::holder_balance_key(&creator, &holder);
513+
let dividend_key = constants::storage::dividend_accumulator(&creator);
514+
515+
assert_ne!(
516+
balance_key, dividend_key,
517+
"holder balance key must not collide with dividend accumulator key"
518+
);
519+
}
520+
521+
#[test]
522+
fn test_holder_dividend_checkpoint_different_holders_differ() {
523+
let env = Env::default();
524+
let creator = Address::generate(&env);
525+
let holder_a = Address::generate(&env);
526+
let holder_b = Address::generate(&env);
527+
528+
let key_a = constants::storage::holder_dividend_checkpoint(&creator, &holder_a);
529+
let key_b = constants::storage::holder_dividend_checkpoint(&creator, &holder_b);
530+
531+
assert_ne!(
532+
key_a, key_b,
533+
"different holders must produce different dividend checkpoint keys"
534+
);
535+
}
536+
537+
#[test]
538+
fn test_holder_dividend_pending_different_holders_differ() {
539+
let env = Env::default();
540+
let creator = Address::generate(&env);
541+
let holder_a = Address::generate(&env);
542+
let holder_b = Address::generate(&env);
543+
544+
let key_a = constants::storage::holder_dividend_pending(&creator, &holder_a);
545+
let key_b = constants::storage::holder_dividend_pending(&creator, &holder_b);
546+
547+
assert_ne!(
548+
key_a, key_b,
549+
"different holders must produce different dividend pending keys"
550+
);
551+
}
552+
553+
#[test]
554+
fn test_co_creator_fee_balance_different_pairs_differ() {
555+
let env = Env::default();
556+
let creator = Address::generate(&env);
557+
let co_a = Address::generate(&env);
558+
let co_b = Address::generate(&env);
559+
560+
let key_a = constants::storage::co_creator_fee_balance(&creator, &co_a);
561+
let key_b = constants::storage::co_creator_fee_balance(&creator, &co_b);
562+
563+
assert_ne!(
564+
key_a, key_b,
565+
"different co-creator addresses must produce different fee balance keys"
566+
);
567+
}
568+
569+
#[test]
570+
fn test_co_creator_fee_balance_different_creators_differ() {
571+
let env = Env::default();
572+
let creator_a = Address::generate(&env);
573+
let creator_b = Address::generate(&env);
574+
let co_creator = Address::generate(&env);
575+
576+
let key_a = constants::storage::co_creator_fee_balance(&creator_a, &co_creator);
577+
let key_b = constants::storage::co_creator_fee_balance(&creator_b, &co_creator);
578+
579+
assert_ne!(
580+
key_a, key_b,
581+
"different creators with same co-creator must produce different keys"
582+
);
583+
}
584+
585+
#[test]
586+
fn test_creator_fee_balance_different_creators_differ() {
587+
let env = Env::default();
588+
let creator_a = Address::generate(&env);
589+
let creator_b = Address::generate(&env);
590+
591+
let key_a = constants::storage::creator_fee_balance(&creator_a);
592+
let key_b = constants::storage::creator_fee_balance(&creator_b);
593+
594+
assert_ne!(
595+
key_a, key_b,
596+
"different creators must produce different fee balance keys"
597+
);
598+
}
599+
600+
#[test]
601+
fn test_holder_balance_key_never_equals_creator_fee_balance_key() {
602+
let env = Env::default();
603+
let creator = Address::generate(&env);
604+
let holder = Address::generate(&env);
605+
606+
let balance_key = constants::storage::holder_balance_key(&creator, &holder);
607+
let fee_key = constants::storage::creator_fee_balance(&creator);
608+
609+
assert_ne!(
610+
balance_key, fee_key,
611+
"holder balance key must not collide with creator fee balance key"
612+
);
613+
}
614+
615+
#[test]
616+
fn test_holder_dividend_checkpoint_never_equals_holder_balance_key() {
617+
let env = Env::default();
618+
let creator = Address::generate(&env);
619+
let holder = Address::generate(&env);
620+
621+
let checkpoint_key = constants::storage::holder_dividend_checkpoint(&creator, &holder);
622+
let balance_key = constants::storage::holder_balance_key(&creator, &holder);
623+
624+
assert_ne!(
625+
checkpoint_key, balance_key,
626+
"dividend checkpoint key must not collide with holder balance key"
627+
);
628+
}
629+
630+
#[test]
631+
fn test_holder_dividend_pending_never_equals_holder_balance_key() {
632+
let env = Env::default();
633+
let creator = Address::generate(&env);
634+
let holder = Address::generate(&env);
635+
636+
let pending_key = constants::storage::holder_dividend_pending(&creator, &holder);
637+
let balance_key = constants::storage::holder_balance_key(&creator, &holder);
638+
639+
assert_ne!(
640+
pending_key, balance_key,
641+
"dividend pending key must not collide with holder balance key"
642+
);
643+
}
644+
645+
#[test]
646+
fn test_curve_preset_different_creators_differ() {
647+
let env = Env::default();
648+
let creator_a = Address::generate(&env);
649+
let creator_b = Address::generate(&env);
650+
651+
let key_a = constants::storage::curve_preset(&creator_a);
652+
let key_b = constants::storage::curve_preset(&creator_b);
653+
654+
assert_ne!(
655+
key_a, key_b,
656+
"different creators must produce different curve preset keys"
657+
);
658+
}
659+
660+
#[test]
661+
fn test_max_supply_different_creators_differ() {
662+
let env = Env::default();
663+
let creator_a = Address::generate(&env);
664+
let creator_b = Address::generate(&env);
665+
666+
let key_a = constants::storage::max_supply(&creator_a);
667+
let key_b = constants::storage::max_supply(&creator_b);
668+
669+
assert_ne!(
670+
key_a, key_b,
671+
"different creators must produce different max supply keys"
672+
);
673+
}
674+
675+
#[test]
676+
fn test_max_keys_per_wallet_different_creators_differ() {
677+
let env = Env::default();
678+
let creator_a = Address::generate(&env);
679+
let creator_b = Address::generate(&env);
680+
681+
let key_a = constants::storage::max_keys_per_wallet(&creator_a);
682+
let key_b = constants::storage::max_keys_per_wallet(&creator_b);
683+
684+
assert_ne!(
685+
key_a, key_b,
686+
"different creators must produce different max keys per wallet keys"
687+
);
688+
}
689+
690+
#[test]
691+
fn test_locked_allocation_different_creators_differ() {
692+
let env = Env::default();
693+
let creator_a = Address::generate(&env);
694+
let creator_b = Address::generate(&env);
695+
696+
let key_a = constants::storage::locked_allocation(&creator_a);
697+
let key_b = constants::storage::locked_allocation(&creator_b);
698+
699+
assert_ne!(
700+
key_a, key_b,
701+
"different creators must produce different locked allocation keys"
702+
);
703+
}
453704
}

0 commit comments

Comments
 (0)