fix: validate authority on self-transfer early return#2252
fix: validate authority on self-transfer early return#2252ananas-block merged 6 commits intomainfrom
Conversation
Validate that the authority is a signer and is the owner or delegate before allowing self-transfer early return. Previously the self-transfer path returned Ok(()) without any authority validation.
📝 WalkthroughWalkthroughAdds self-transfer validation: a new public Changes
Sequence Diagram(s)sequenceDiagram
actor Caller
participant Transfer as Transfer Handler
participant Validator as Self-Transfer Validator
participant Processor as Transfer Processor
Caller->>Transfer: initiate_transfer(source, destination, authority)
Transfer->>Validator: validate_self_transfer(source, destination, authority)
alt source == destination
Validator->>Validator: validate authority (signer & owner/delegate)
alt authority valid
Validator-->>Transfer: Ok(true)
Transfer-->>Caller: Ok(()) rgba(0,128,0,0.5)
else authority invalid
Validator-->>Transfer: Err(InvalidAccountData)
Transfer-->>Caller: Err(InvalidAccountData) rgba(255,0,0,0.5)
end
else source != destination
Validator-->>Transfer: Ok(false)
Transfer->>Processor: continue_normal_transfer_flow()
Processor-->>Caller: Ok(()) rgba(0,0,255,0.5)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Extract duplicate self-transfer check from default.rs and checked.rs into validate_self_transfer() in shared.rs with cold path for authority validation.
Random key generation could produce duplicate keys, causing DuplicateMetadataKey error (18040) with certain seeds.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@programs/compressed-token/program/src/ctoken/transfer/default.rs`:
- Around line 46-51: The self-transfer authority check currently implemented in
shared.rs as validate_self_transfer_authority should be refactored to call the
shared utility verify_owner_or_delegate_signer from shared/owner_validation.rs
instead of reimplementing owner/delegate checks; update
validate_self_transfer_authority to invoke verify_owner_or_delegate_signer with
the same account slices/authority arguments used elsewhere (matching usages in
token_input.rs and compress_or_decompress_ctokens.rs), propagate and return its
Result/error directly, and remove the duplicated manual owner/delegate logic so
callers (like the code that currently calls validate_self_transfer) keep the
same behavior but rely on the centralized verifier.
| let is_delegate = token | ||
| .base | ||
| .delegate() | ||
| .is_some_and(|d| pubkey_eq(authority.key(), d.array_ref())); |
There was a problem hiding this comment.
In SPL, it will check if the delegate amount is greater than the transferred amount, even when self-transferring. So there's a bit of inconsistency here
* fix: handle self-transfer in ctoken transfer and transfer_checked Validate that the authority is a signer and is the owner or delegate before allowing self-transfer early return. Previously the self-transfer path returned Ok(()) without any authority validation. * fix: simplify map_or to is_some_and per clippy * fix: use pubkey_eq for self-transfer check * refactor: extract self-transfer validation into shared function Extract duplicate self-transfer check from default.rs and checked.rs into validate_self_transfer() in shared.rs with cold path for authority validation. * chore: format * fix: deduplicate random metadata keys in test_random_mint_action Random key generation could produce duplicate keys, causing DuplicateMetadataKey error (18040) with certain seeds.
refactor remove fetch-accounts renaming, simplify trait fomat excl photon-api submodule fix: multi-pass cold account lookup in test indexer RPC Align get_account_interface and get_multiple_account_interfaces with Photon's lookup strategy: search compressed_accounts by onchain_pubkey, then by PDA seed derivation, then token_compressed_accounts, then by token_data.owner. Also fix as_mint() to accept ColdContext::Account since Photon returns mints as generic compressed accounts. Co-authored-by: Cursor <cursoragent@cursor.com> lint fix lint fix: reject rent sponsor self-referencing the token account (#2257) * fix: reject rent sponsor self-referencing the token account Audit issue #9 (INFO): The rent payer could be the same account as the target token account being created. Add a check that rejects this self-reference to prevent accounting issues. * test: add failing test rent sponsor self reference fix: process metadata add/remove actions in sequential order (#2256) * fix: process metadata add/remove actions in sequential order Audit issue #16 (LOW): should_add_key checked for any add and any remove independently, ignoring action ordering. An add-remove-add sequence would incorrectly remove the key. Process actions sequentially so the final state reflects the actual order. * chore: format * test: add randomized test for metadata action processing Validates that process_extensions_config_with_actions produces correct AdditionalMetadataConfig for random sequences of UpdateMetadataField and RemoveMetadataKey actions, covering the add-remove-add bug from audit issue #16. * test: add integration test for audit issue #13 (no double rent charge) Verifies that two compress operations targeting the same compressible CToken account in a single Transfer2 instruction do not double-charge the rent top-up budget. * chore: format extensions_metadata test fix: validate authority on self-transfer early return (#2252) * fix: handle self-transfer in ctoken transfer and transfer_checked Validate that the authority is a signer and is the owner or delegate before allowing self-transfer early return. Previously the self-transfer path returned Ok(()) without any authority validation. * fix: simplify map_or to is_some_and per clippy * fix: use pubkey_eq for self-transfer check * refactor: extract self-transfer validation into shared function Extract duplicate self-transfer check from default.rs and checked.rs into validate_self_transfer() in shared.rs with cold path for authority validation. * chore: format * fix: deduplicate random metadata keys in test_random_mint_action Random key generation could produce duplicate keys, causing DuplicateMetadataKey error (18040) with certain seeds. fix: enforce mint extension checks in cToken-to-cToken decompress (#2246) * fix: enforce mint extension checks in cToken-to-cToken decompress hot path Add enforce_extension_state() to MintExtensionChecks and call it in the Decompress branch when decompress_inputs is None (hot-path, not CompressedOnly restore). This prevents cToken-to-cToken transfers from bypassing pause, transfer fee, and transfer hook restrictions. * fix test chore: reject compress for mints with restricted extensions in build_mint_extension_cache (#2240) * chore: reject compress for mints with restricted extensions in mint check * Update programs/compressed-token/program/src/compressed_token/transfer2/check_extensions.rs Co-authored-by: 0xa5df-c <172008956+0xa5df-c@users.noreply.github.com> * fix: format else-if condition for lint --------- Co-authored-by: 0xa5df-c <172008956+0xa5df-c@users.noreply.github.com> fix: token-pool index 0 check (#2239) fix(programs): add MintCloseAuthority as restricted extension (M-03) (#2263) * fix: add MintCloseAuthority as restricted extension (M-03) A mint with MintCloseAuthority can be closed and re-opened with different extensions. Treating it as restricted ensures compressed tokens from such mints require CompressedOnly mode. * test: add MintCloseAuthority compression_only requirement tests Add test coverage for MintCloseAuthority requiring compression_only mode, complementing the fix in f2da063. refactor: light program pinocchio macro (#2247) * refactor: light program pinocchio macro * fix: address CodeRabbit review comments on macro codegen - Fix account order bug in process_update_config (config=0, authority=1) - Use backend-provided serialize/deserialize derives in LightAccountData - Remove redundant is_pinocchio() branch for has_le_bytes unpack fields - DRY doc attribute generation across 4 struct generation methods - Unify unpack_data path using account_crate re-export for both backends chore(libs): bump versions (#2272) fix(programs): allow account-level delegate to compress CToken (M-02) (#2262) * fix: allow account-level delegate to compress tokens from CToken (M-02) check_ctoken_owner() only checked owner and permanent delegate. An account-level delegate (approved via CTokenApprove) could not compress tokens. Added delegate check after permanent delegate. * test: compress by delegate fix: accumulate delegated amount at decompression (#2242) * fix: accumulate delegated amount at decompression * fix lint * refactor: simplify apply_delegate to single accumulation path * fix: ignore delegated_amount without delegate * restore decompress amount check fix programtest, wallet owner tracking for ata fmt and lint
refactor remove fetch-accounts renaming, simplify trait fomat excl photon-api submodule fix: multi-pass cold account lookup in test indexer RPC Align get_account_interface and get_multiple_account_interfaces with Photon's lookup strategy: search compressed_accounts by onchain_pubkey, then by PDA seed derivation, then token_compressed_accounts, then by token_data.owner. Also fix as_mint() to accept ColdContext::Account since Photon returns mints as generic compressed accounts. Co-authored-by: Cursor <cursoragent@cursor.com> lint fix lint fix: reject rent sponsor self-referencing the token account (#2257) * fix: reject rent sponsor self-referencing the token account Audit issue #9 (INFO): The rent payer could be the same account as the target token account being created. Add a check that rejects this self-reference to prevent accounting issues. * test: add failing test rent sponsor self reference fix: process metadata add/remove actions in sequential order (#2256) * fix: process metadata add/remove actions in sequential order Audit issue #16 (LOW): should_add_key checked for any add and any remove independently, ignoring action ordering. An add-remove-add sequence would incorrectly remove the key. Process actions sequentially so the final state reflects the actual order. * chore: format * test: add randomized test for metadata action processing Validates that process_extensions_config_with_actions produces correct AdditionalMetadataConfig for random sequences of UpdateMetadataField and RemoveMetadataKey actions, covering the add-remove-add bug from audit issue #16. * test: add integration test for audit issue #13 (no double rent charge) Verifies that two compress operations targeting the same compressible CToken account in a single Transfer2 instruction do not double-charge the rent top-up budget. * chore: format extensions_metadata test fix: validate authority on self-transfer early return (#2252) * fix: handle self-transfer in ctoken transfer and transfer_checked Validate that the authority is a signer and is the owner or delegate before allowing self-transfer early return. Previously the self-transfer path returned Ok(()) without any authority validation. * fix: simplify map_or to is_some_and per clippy * fix: use pubkey_eq for self-transfer check * refactor: extract self-transfer validation into shared function Extract duplicate self-transfer check from default.rs and checked.rs into validate_self_transfer() in shared.rs with cold path for authority validation. * chore: format * fix: deduplicate random metadata keys in test_random_mint_action Random key generation could produce duplicate keys, causing DuplicateMetadataKey error (18040) with certain seeds. fix: enforce mint extension checks in cToken-to-cToken decompress (#2246) * fix: enforce mint extension checks in cToken-to-cToken decompress hot path Add enforce_extension_state() to MintExtensionChecks and call it in the Decompress branch when decompress_inputs is None (hot-path, not CompressedOnly restore). This prevents cToken-to-cToken transfers from bypassing pause, transfer fee, and transfer hook restrictions. * fix test chore: reject compress for mints with restricted extensions in build_mint_extension_cache (#2240) * chore: reject compress for mints with restricted extensions in mint check * Update programs/compressed-token/program/src/compressed_token/transfer2/check_extensions.rs Co-authored-by: 0xa5df-c <172008956+0xa5df-c@users.noreply.github.com> * fix: format else-if condition for lint --------- Co-authored-by: 0xa5df-c <172008956+0xa5df-c@users.noreply.github.com> fix: token-pool index 0 check (#2239) fix(programs): add MintCloseAuthority as restricted extension (M-03) (#2263) * fix: add MintCloseAuthority as restricted extension (M-03) A mint with MintCloseAuthority can be closed and re-opened with different extensions. Treating it as restricted ensures compressed tokens from such mints require CompressedOnly mode. * test: add MintCloseAuthority compression_only requirement tests Add test coverage for MintCloseAuthority requiring compression_only mode, complementing the fix in f2da063. refactor: light program pinocchio macro (#2247) * refactor: light program pinocchio macro * fix: address CodeRabbit review comments on macro codegen - Fix account order bug in process_update_config (config=0, authority=1) - Use backend-provided serialize/deserialize derives in LightAccountData - Remove redundant is_pinocchio() branch for has_le_bytes unpack fields - DRY doc attribute generation across 4 struct generation methods - Unify unpack_data path using account_crate re-export for both backends chore(libs): bump versions (#2272) fix(programs): allow account-level delegate to compress CToken (M-02) (#2262) * fix: allow account-level delegate to compress tokens from CToken (M-02) check_ctoken_owner() only checked owner and permanent delegate. An account-level delegate (approved via CTokenApprove) could not compress tokens. Added delegate check after permanent delegate. * test: compress by delegate fix: accumulate delegated amount at decompression (#2242) * fix: accumulate delegated amount at decompression * fix lint * refactor: simplify apply_delegate to single accumulation path * fix: ignore delegated_amount without delegate * restore decompress amount check fix programtest, wallet owner tracking for ata fmt and lint
refactor remove fetch-accounts renaming, simplify trait fomat excl photon-api submodule fix: multi-pass cold account lookup in test indexer RPC Align get_account_interface and get_multiple_account_interfaces with Photon's lookup strategy: search compressed_accounts by onchain_pubkey, then by PDA seed derivation, then token_compressed_accounts, then by token_data.owner. Also fix as_mint() to accept ColdContext::Account since Photon returns mints as generic compressed accounts. Co-authored-by: Cursor <cursoragent@cursor.com> lint fix lint fix: reject rent sponsor self-referencing the token account (#2257) * fix: reject rent sponsor self-referencing the token account Audit issue #9 (INFO): The rent payer could be the same account as the target token account being created. Add a check that rejects this self-reference to prevent accounting issues. * test: add failing test rent sponsor self reference fix: process metadata add/remove actions in sequential order (#2256) * fix: process metadata add/remove actions in sequential order Audit issue #16 (LOW): should_add_key checked for any add and any remove independently, ignoring action ordering. An add-remove-add sequence would incorrectly remove the key. Process actions sequentially so the final state reflects the actual order. * chore: format * test: add randomized test for metadata action processing Validates that process_extensions_config_with_actions produces correct AdditionalMetadataConfig for random sequences of UpdateMetadataField and RemoveMetadataKey actions, covering the add-remove-add bug from audit issue #16. * test: add integration test for audit issue #13 (no double rent charge) Verifies that two compress operations targeting the same compressible CToken account in a single Transfer2 instruction do not double-charge the rent top-up budget. * chore: format extensions_metadata test fix: validate authority on self-transfer early return (#2252) * fix: handle self-transfer in ctoken transfer and transfer_checked Validate that the authority is a signer and is the owner or delegate before allowing self-transfer early return. Previously the self-transfer path returned Ok(()) without any authority validation. * fix: simplify map_or to is_some_and per clippy * fix: use pubkey_eq for self-transfer check * refactor: extract self-transfer validation into shared function Extract duplicate self-transfer check from default.rs and checked.rs into validate_self_transfer() in shared.rs with cold path for authority validation. * chore: format * fix: deduplicate random metadata keys in test_random_mint_action Random key generation could produce duplicate keys, causing DuplicateMetadataKey error (18040) with certain seeds. fix: enforce mint extension checks in cToken-to-cToken decompress (#2246) * fix: enforce mint extension checks in cToken-to-cToken decompress hot path Add enforce_extension_state() to MintExtensionChecks and call it in the Decompress branch when decompress_inputs is None (hot-path, not CompressedOnly restore). This prevents cToken-to-cToken transfers from bypassing pause, transfer fee, and transfer hook restrictions. * fix test chore: reject compress for mints with restricted extensions in build_mint_extension_cache (#2240) * chore: reject compress for mints with restricted extensions in mint check * Update programs/compressed-token/program/src/compressed_token/transfer2/check_extensions.rs Co-authored-by: 0xa5df-c <172008956+0xa5df-c@users.noreply.github.com> * fix: format else-if condition for lint --------- Co-authored-by: 0xa5df-c <172008956+0xa5df-c@users.noreply.github.com> fix: token-pool index 0 check (#2239) fix(programs): add MintCloseAuthority as restricted extension (M-03) (#2263) * fix: add MintCloseAuthority as restricted extension (M-03) A mint with MintCloseAuthority can be closed and re-opened with different extensions. Treating it as restricted ensures compressed tokens from such mints require CompressedOnly mode. * test: add MintCloseAuthority compression_only requirement tests Add test coverage for MintCloseAuthority requiring compression_only mode, complementing the fix in f2da063. refactor: light program pinocchio macro (#2247) * refactor: light program pinocchio macro * fix: address CodeRabbit review comments on macro codegen - Fix account order bug in process_update_config (config=0, authority=1) - Use backend-provided serialize/deserialize derives in LightAccountData - Remove redundant is_pinocchio() branch for has_le_bytes unpack fields - DRY doc attribute generation across 4 struct generation methods - Unify unpack_data path using account_crate re-export for both backends chore(libs): bump versions (#2272) fix(programs): allow account-level delegate to compress CToken (M-02) (#2262) * fix: allow account-level delegate to compress tokens from CToken (M-02) check_ctoken_owner() only checked owner and permanent delegate. An account-level delegate (approved via CTokenApprove) could not compress tokens. Added delegate check after permanent delegate. * test: compress by delegate fix: accumulate delegated amount at decompression (#2242) * fix: accumulate delegated amount at decompression * fix lint * refactor: simplify apply_delegate to single accumulation path * fix: ignore delegated_amount without delegate * restore decompress amount check fix programtest, wallet owner tracking for ata fmt and lint upd amm test simplify client usage, remove unnecessary endpoints clean cleanup lint
refactor remove fetch-accounts renaming, simplify trait fomat excl photon-api submodule fix: multi-pass cold account lookup in test indexer RPC Align get_account_interface and get_multiple_account_interfaces with Photon's lookup strategy: search compressed_accounts by onchain_pubkey, then by PDA seed derivation, then token_compressed_accounts, then by token_data.owner. Also fix as_mint() to accept ColdContext::Account since Photon returns mints as generic compressed accounts. Co-authored-by: Cursor <cursoragent@cursor.com> lint fix lint fix: reject rent sponsor self-referencing the token account (#2257) * fix: reject rent sponsor self-referencing the token account Audit issue #9 (INFO): The rent payer could be the same account as the target token account being created. Add a check that rejects this self-reference to prevent accounting issues. * test: add failing test rent sponsor self reference fix: process metadata add/remove actions in sequential order (#2256) * fix: process metadata add/remove actions in sequential order Audit issue #16 (LOW): should_add_key checked for any add and any remove independently, ignoring action ordering. An add-remove-add sequence would incorrectly remove the key. Process actions sequentially so the final state reflects the actual order. * chore: format * test: add randomized test for metadata action processing Validates that process_extensions_config_with_actions produces correct AdditionalMetadataConfig for random sequences of UpdateMetadataField and RemoveMetadataKey actions, covering the add-remove-add bug from audit issue #16. * test: add integration test for audit issue #13 (no double rent charge) Verifies that two compress operations targeting the same compressible CToken account in a single Transfer2 instruction do not double-charge the rent top-up budget. * chore: format extensions_metadata test fix: validate authority on self-transfer early return (#2252) * fix: handle self-transfer in ctoken transfer and transfer_checked Validate that the authority is a signer and is the owner or delegate before allowing self-transfer early return. Previously the self-transfer path returned Ok(()) without any authority validation. * fix: simplify map_or to is_some_and per clippy * fix: use pubkey_eq for self-transfer check * refactor: extract self-transfer validation into shared function Extract duplicate self-transfer check from default.rs and checked.rs into validate_self_transfer() in shared.rs with cold path for authority validation. * chore: format * fix: deduplicate random metadata keys in test_random_mint_action Random key generation could produce duplicate keys, causing DuplicateMetadataKey error (18040) with certain seeds. fix: enforce mint extension checks in cToken-to-cToken decompress (#2246) * fix: enforce mint extension checks in cToken-to-cToken decompress hot path Add enforce_extension_state() to MintExtensionChecks and call it in the Decompress branch when decompress_inputs is None (hot-path, not CompressedOnly restore). This prevents cToken-to-cToken transfers from bypassing pause, transfer fee, and transfer hook restrictions. * fix test chore: reject compress for mints with restricted extensions in build_mint_extension_cache (#2240) * chore: reject compress for mints with restricted extensions in mint check * Update programs/compressed-token/program/src/compressed_token/transfer2/check_extensions.rs Co-authored-by: 0xa5df-c <172008956+0xa5df-c@users.noreply.github.com> * fix: format else-if condition for lint --------- Co-authored-by: 0xa5df-c <172008956+0xa5df-c@users.noreply.github.com> fix: token-pool index 0 check (#2239) fix(programs): add MintCloseAuthority as restricted extension (M-03) (#2263) * fix: add MintCloseAuthority as restricted extension (M-03) A mint with MintCloseAuthority can be closed and re-opened with different extensions. Treating it as restricted ensures compressed tokens from such mints require CompressedOnly mode. * test: add MintCloseAuthority compression_only requirement tests Add test coverage for MintCloseAuthority requiring compression_only mode, complementing the fix in f2da063. refactor: light program pinocchio macro (#2247) * refactor: light program pinocchio macro * fix: address CodeRabbit review comments on macro codegen - Fix account order bug in process_update_config (config=0, authority=1) - Use backend-provided serialize/deserialize derives in LightAccountData - Remove redundant is_pinocchio() branch for has_le_bytes unpack fields - DRY doc attribute generation across 4 struct generation methods - Unify unpack_data path using account_crate re-export for both backends chore(libs): bump versions (#2272) fix(programs): allow account-level delegate to compress CToken (M-02) (#2262) * fix: allow account-level delegate to compress tokens from CToken (M-02) check_ctoken_owner() only checked owner and permanent delegate. An account-level delegate (approved via CTokenApprove) could not compress tokens. Added delegate check after permanent delegate. * test: compress by delegate fix: accumulate delegated amount at decompression (#2242) * fix: accumulate delegated amount at decompression * fix lint * refactor: simplify apply_delegate to single accumulation path * fix: ignore delegated_amount without delegate * restore decompress amount check fix programtest, wallet owner tracking for ata fmt and lint upd amm test simplify client usage, remove unnecessary endpoints clean cleanup lint
Summary
CTokenTransferandCTokenTransferChecked. Previously the self-transfer path returnedOk(())without any authority validation, allowing anyone to call self-transfer on any account.validate_self_transfer()to avoid duplicate code across both transfer variants.Summary by CodeRabbit